diff options
| -rw-r--r-- | arch/x86/Kconfig.debug | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/mmiotrace/mmio-mod.c | 140 | ||||
| -rw-r--r-- | include/linux/mmiotrace.h | 85 | ||||
| -rw-r--r-- | kernel/trace/trace.c | 34 | ||||
| -rw-r--r-- | kernel/trace/trace.h | 14 | ||||
| -rw-r--r-- | kernel/trace/trace_mmiotrace.c | 151 |
6 files changed, 238 insertions, 188 deletions
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 7e4b8494078e..1d6de0d67f99 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug | |||
| @@ -173,7 +173,7 @@ config MMIOTRACE_HOOKS | |||
| 173 | 173 | ||
| 174 | config MMIOTRACE | 174 | config MMIOTRACE |
| 175 | bool "Memory mapped IO tracing" | 175 | bool "Memory mapped IO tracing" |
| 176 | depends on DEBUG_KERNEL && RELAY | 176 | depends on DEBUG_KERNEL |
| 177 | select TRACING | 177 | select TRACING |
| 178 | select MMIOTRACE_HOOKS | 178 | select MMIOTRACE_HOOKS |
| 179 | default y | 179 | default y |
diff --git a/arch/x86/kernel/mmiotrace/mmio-mod.c b/arch/x86/kernel/mmiotrace/mmio-mod.c index c7a67d7e482b..62abc281a512 100644 --- a/arch/x86/kernel/mmiotrace/mmio-mod.c +++ b/arch/x86/kernel/mmiotrace/mmio-mod.c | |||
| @@ -37,11 +37,6 @@ | |||
| 37 | 37 | ||
| 38 | #define NAME "mmiotrace: " | 38 | #define NAME "mmiotrace: " |
| 39 | 39 | ||
| 40 | /* This app's relay channel files will appear in /debug/mmio-trace */ | ||
| 41 | static const char APP_DIR[] = "mmio-trace"; | ||
| 42 | /* the marker injection file in /debug/APP_DIR */ | ||
| 43 | static const char MARKER_FILE[] = "mmio-marker"; | ||
| 44 | |||
| 45 | struct trap_reason { | 40 | struct trap_reason { |
| 46 | unsigned long addr; | 41 | unsigned long addr; |
| 47 | unsigned long ip; | 42 | unsigned long ip; |
| @@ -56,18 +51,15 @@ struct remap_trace { | |||
| 56 | unsigned long id; | 51 | unsigned long id; |
| 57 | }; | 52 | }; |
| 58 | 53 | ||
| 59 | static const size_t subbuf_size = 256*1024; | ||
| 60 | |||
| 61 | /* Accessed per-cpu. */ | 54 | /* Accessed per-cpu. */ |
| 62 | static DEFINE_PER_CPU(struct trap_reason, pf_reason); | 55 | static DEFINE_PER_CPU(struct trap_reason, pf_reason); |
| 63 | static DEFINE_PER_CPU(struct mm_io_header_rw, cpu_trace); | 56 | static DEFINE_PER_CPU(struct mmiotrace_rw, cpu_trace); |
| 64 | 57 | ||
| 65 | #if 0 /* XXX: no way gather this info anymore */ | 58 | #if 0 /* XXX: no way gather this info anymore */ |
| 66 | /* Access to this is not per-cpu. */ | 59 | /* Access to this is not per-cpu. */ |
| 67 | static DEFINE_PER_CPU(atomic_t, dropped); | 60 | static DEFINE_PER_CPU(atomic_t, dropped); |
| 68 | #endif | 61 | #endif |
| 69 | 62 | ||
| 70 | static struct dentry *dir; | ||
| 71 | static struct dentry *marker_file; | 63 | static struct dentry *marker_file; |
| 72 | 64 | ||
| 73 | static DEFINE_MUTEX(mmiotrace_mutex); | 65 | static DEFINE_MUTEX(mmiotrace_mutex); |
| @@ -82,24 +74,21 @@ static LIST_HEAD(trace_list); /* struct remap_trace */ | |||
| 82 | * and trace_lock. | 74 | * and trace_lock. |
| 83 | * - Routines depending on is_enabled() must take trace_lock. | 75 | * - Routines depending on is_enabled() must take trace_lock. |
| 84 | * - trace_list users must hold trace_lock. | 76 | * - trace_list users must hold trace_lock. |
| 85 | * - is_enabled() guarantees that chan is valid. | 77 | * - is_enabled() guarantees that mmio_trace_record is allowed. |
| 86 | * - pre/post callbacks assume the effect of is_enabled() being true. | 78 | * - pre/post callbacks assume the effect of is_enabled() being true. |
| 87 | */ | 79 | */ |
| 88 | 80 | ||
| 89 | /* module parameters */ | 81 | /* module parameters */ |
| 90 | static unsigned int n_subbufs = 32*4; | ||
| 91 | static unsigned long filter_offset; | 82 | static unsigned long filter_offset; |
| 92 | static int nommiotrace; | 83 | static int nommiotrace; |
| 93 | static int ISA_trace; | 84 | static int ISA_trace; |
| 94 | static int trace_pc; | 85 | static int trace_pc; |
| 95 | 86 | ||
| 96 | module_param(n_subbufs, uint, 0); | ||
| 97 | module_param(filter_offset, ulong, 0); | 87 | module_param(filter_offset, ulong, 0); |
| 98 | module_param(nommiotrace, bool, 0); | 88 | module_param(nommiotrace, bool, 0); |
| 99 | module_param(ISA_trace, bool, 0); | 89 | module_param(ISA_trace, bool, 0); |
| 100 | module_param(trace_pc, bool, 0); | 90 | module_param(trace_pc, bool, 0); |
| 101 | 91 | ||
| 102 | MODULE_PARM_DESC(n_subbufs, "Number of 256kB buffers, default 128."); | ||
| 103 | MODULE_PARM_DESC(filter_offset, "Start address of traced mappings."); | 92 | MODULE_PARM_DESC(filter_offset, "Start address of traced mappings."); |
| 104 | MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing."); | 93 | MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing."); |
| 105 | MODULE_PARM_DESC(ISA_trace, "Do not exclude the low ISA range."); | 94 | MODULE_PARM_DESC(ISA_trace, "Do not exclude the low ISA range."); |
| @@ -110,6 +99,7 @@ static bool is_enabled(void) | |||
| 110 | return atomic_read(&mmiotrace_enabled); | 99 | return atomic_read(&mmiotrace_enabled); |
| 111 | } | 100 | } |
| 112 | 101 | ||
| 102 | #if 0 /* XXX: needs rewrite */ | ||
| 113 | /* | 103 | /* |
| 114 | * Write callback for the debugfs entry: | 104 | * Write callback for the debugfs entry: |
| 115 | * Read a marker and write it to the mmio trace log | 105 | * Read a marker and write it to the mmio trace log |
| @@ -145,6 +135,7 @@ static ssize_t write_marker(struct file *file, const char __user *buffer, | |||
| 145 | kfree(event); | 135 | kfree(event); |
| 146 | return len; | 136 | return len; |
| 147 | } | 137 | } |
| 138 | #endif | ||
| 148 | 139 | ||
| 149 | static void print_pte(unsigned long address) | 140 | static void print_pte(unsigned long address) |
| 150 | { | 141 | { |
| @@ -198,9 +189,10 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs, | |||
| 198 | unsigned long addr) | 189 | unsigned long addr) |
| 199 | { | 190 | { |
| 200 | struct trap_reason *my_reason = &get_cpu_var(pf_reason); | 191 | struct trap_reason *my_reason = &get_cpu_var(pf_reason); |
| 201 | struct mm_io_header_rw *my_trace = &get_cpu_var(cpu_trace); | 192 | struct mmiotrace_rw *my_trace = &get_cpu_var(cpu_trace); |
| 202 | const unsigned long instptr = instruction_pointer(regs); | 193 | const unsigned long instptr = instruction_pointer(regs); |
| 203 | const enum reason_type type = get_ins_type(instptr); | 194 | const enum reason_type type = get_ins_type(instptr); |
| 195 | struct remap_trace *trace = p->user_data; | ||
| 204 | 196 | ||
| 205 | /* it doesn't make sense to have more than one active trace per cpu */ | 197 | /* it doesn't make sense to have more than one active trace per cpu */ |
| 206 | if (my_reason->active_traces) | 198 | if (my_reason->active_traces) |
| @@ -212,23 +204,17 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs, | |||
| 212 | my_reason->addr = addr; | 204 | my_reason->addr = addr; |
| 213 | my_reason->ip = instptr; | 205 | my_reason->ip = instptr; |
| 214 | 206 | ||
| 215 | my_trace->header.type = MMIO_MAGIC; | 207 | my_trace->phys = addr - trace->probe.addr + trace->phys; |
| 216 | my_trace->header.pid = 0; | 208 | my_trace->map_id = trace->id; |
| 217 | my_trace->header.data_len = sizeof(struct mm_io_rw); | ||
| 218 | my_trace->rw.address = addr; | ||
| 219 | /* | ||
| 220 | * struct remap_trace *trace = p->user_data; | ||
| 221 | * phys = addr - trace->probe.addr + trace->phys; | ||
| 222 | */ | ||
| 223 | 209 | ||
| 224 | /* | 210 | /* |
| 225 | * Only record the program counter when requested. | 211 | * Only record the program counter when requested. |
| 226 | * It may taint clean-room reverse engineering. | 212 | * It may taint clean-room reverse engineering. |
| 227 | */ | 213 | */ |
| 228 | if (trace_pc) | 214 | if (trace_pc) |
| 229 | my_trace->rw.pc = instptr; | 215 | my_trace->pc = instptr; |
| 230 | else | 216 | else |
| 231 | my_trace->rw.pc = 0; | 217 | my_trace->pc = 0; |
| 232 | 218 | ||
| 233 | /* | 219 | /* |
| 234 | * XXX: the timestamp recorded will be *after* the tracing has been | 220 | * XXX: the timestamp recorded will be *after* the tracing has been |
| @@ -238,28 +224,25 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs, | |||
| 238 | 224 | ||
| 239 | switch (type) { | 225 | switch (type) { |
| 240 | case REG_READ: | 226 | case REG_READ: |
| 241 | my_trace->header.type |= | 227 | my_trace->opcode = MMIO_READ; |
| 242 | (MMIO_READ << MMIO_OPCODE_SHIFT) | | 228 | my_trace->width = get_ins_mem_width(instptr); |
| 243 | (get_ins_mem_width(instptr) << MMIO_WIDTH_SHIFT); | ||
| 244 | break; | 229 | break; |
| 245 | case REG_WRITE: | 230 | case REG_WRITE: |
| 246 | my_trace->header.type |= | 231 | my_trace->opcode = MMIO_WRITE; |
| 247 | (MMIO_WRITE << MMIO_OPCODE_SHIFT) | | 232 | my_trace->width = get_ins_mem_width(instptr); |
| 248 | (get_ins_mem_width(instptr) << MMIO_WIDTH_SHIFT); | 233 | my_trace->value = get_ins_reg_val(instptr, regs); |
| 249 | my_trace->rw.value = get_ins_reg_val(instptr, regs); | ||
| 250 | break; | 234 | break; |
| 251 | case IMM_WRITE: | 235 | case IMM_WRITE: |
| 252 | my_trace->header.type |= | 236 | my_trace->opcode = MMIO_WRITE; |
| 253 | (MMIO_WRITE << MMIO_OPCODE_SHIFT) | | 237 | my_trace->width = get_ins_mem_width(instptr); |
| 254 | (get_ins_mem_width(instptr) << MMIO_WIDTH_SHIFT); | 238 | my_trace->value = get_ins_imm_val(instptr); |
| 255 | my_trace->rw.value = get_ins_imm_val(instptr); | ||
| 256 | break; | 239 | break; |
| 257 | default: | 240 | default: |
| 258 | { | 241 | { |
| 259 | unsigned char *ip = (unsigned char *)instptr; | 242 | unsigned char *ip = (unsigned char *)instptr; |
| 260 | my_trace->header.type |= | 243 | my_trace->opcode = MMIO_UNKNOWN_OP; |
| 261 | (MMIO_UNKNOWN_OP << MMIO_OPCODE_SHIFT); | 244 | my_trace->width = 0; |
| 262 | my_trace->rw.value = (*ip) << 16 | *(ip + 1) << 8 | | 245 | my_trace->value = (*ip) << 16 | *(ip + 1) << 8 | |
| 263 | *(ip + 2); | 246 | *(ip + 2); |
| 264 | } | 247 | } |
| 265 | } | 248 | } |
| @@ -271,7 +254,7 @@ static void post(struct kmmio_probe *p, unsigned long condition, | |||
| 271 | struct pt_regs *regs) | 254 | struct pt_regs *regs) |
| 272 | { | 255 | { |
| 273 | struct trap_reason *my_reason = &get_cpu_var(pf_reason); | 256 | struct trap_reason *my_reason = &get_cpu_var(pf_reason); |
| 274 | struct mm_io_header_rw *my_trace = &get_cpu_var(cpu_trace); | 257 | struct mmiotrace_rw *my_trace = &get_cpu_var(cpu_trace); |
| 275 | 258 | ||
| 276 | /* this should always return the active_trace count to 0 */ | 259 | /* this should always return the active_trace count to 0 */ |
| 277 | my_reason->active_traces--; | 260 | my_reason->active_traces--; |
| @@ -282,20 +265,13 @@ static void post(struct kmmio_probe *p, unsigned long condition, | |||
| 282 | 265 | ||
| 283 | switch (my_reason->type) { | 266 | switch (my_reason->type) { |
| 284 | case REG_READ: | 267 | case REG_READ: |
| 285 | my_trace->rw.value = get_ins_reg_val(my_reason->ip, regs); | 268 | my_trace->value = get_ins_reg_val(my_reason->ip, regs); |
| 286 | break; | 269 | break; |
| 287 | default: | 270 | default: |
| 288 | break; | 271 | break; |
| 289 | } | 272 | } |
| 290 | 273 | ||
| 291 | /* | 274 | mmio_trace_rw(my_trace); |
| 292 | * XXX: Several required values are ignored: | ||
| 293 | * - mapping id | ||
| 294 | * - program counter | ||
| 295 | * Also the address should be physical, not virtual. | ||
| 296 | */ | ||
| 297 | mmio_trace_record(my_trace->header.type, my_trace->rw.address, | ||
| 298 | my_trace->rw.value); | ||
| 299 | put_cpu_var(cpu_trace); | 275 | put_cpu_var(cpu_trace); |
| 300 | put_cpu_var(pf_reason); | 276 | put_cpu_var(pf_reason); |
| 301 | } | 277 | } |
| @@ -305,21 +281,11 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size, | |||
| 305 | { | 281 | { |
| 306 | static atomic_t next_id; | 282 | static atomic_t next_id; |
| 307 | struct remap_trace *trace = kmalloc(sizeof(*trace), GFP_KERNEL); | 283 | struct remap_trace *trace = kmalloc(sizeof(*trace), GFP_KERNEL); |
| 308 | struct mm_io_header_map event = { | 284 | struct mmiotrace_map map = { |
| 309 | .header = { | 285 | .phys = offset, |
| 310 | .type = MMIO_MAGIC | | 286 | .virt = (unsigned long)addr, |
| 311 | (MMIO_PROBE << MMIO_OPCODE_SHIFT), | 287 | .len = size, |
| 312 | .sec = 0, | 288 | .opcode = MMIO_PROBE |
| 313 | .nsec = 0, | ||
| 314 | .pid = 0, | ||
| 315 | .data_len = sizeof(struct mm_io_map) | ||
| 316 | }, | ||
| 317 | .map = { | ||
| 318 | .phys = offset, | ||
| 319 | .addr = (unsigned long)addr, | ||
| 320 | .len = size, | ||
| 321 | .pc = 0 | ||
| 322 | } | ||
| 323 | }; | 289 | }; |
| 324 | 290 | ||
| 325 | if (!trace) { | 291 | if (!trace) { |
| @@ -338,15 +304,13 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size, | |||
| 338 | .phys = offset, | 304 | .phys = offset, |
| 339 | .id = atomic_inc_return(&next_id) | 305 | .id = atomic_inc_return(&next_id) |
| 340 | }; | 306 | }; |
| 307 | map.map_id = trace->id; | ||
| 341 | 308 | ||
| 342 | spin_lock_irq(&trace_lock); | 309 | spin_lock_irq(&trace_lock); |
| 343 | if (!is_enabled()) | 310 | if (!is_enabled()) |
| 344 | goto not_enabled; | 311 | goto not_enabled; |
| 345 | 312 | ||
| 346 | /* | 313 | mmio_trace_mapping(&map); |
| 347 | * XXX: Insufficient data recorded! | ||
| 348 | */ | ||
| 349 | mmio_trace_record(event.header.type, event.map.addr, event.map.len); | ||
| 350 | list_add_tail(&trace->list, &trace_list); | 314 | list_add_tail(&trace->list, &trace_list); |
| 351 | if (!nommiotrace) | 315 | if (!nommiotrace) |
| 352 | register_kmmio_probe(&trace->probe); | 316 | register_kmmio_probe(&trace->probe); |
| @@ -369,21 +333,11 @@ mmiotrace_ioremap(unsigned long offset, unsigned long size, void __iomem *addr) | |||
| 369 | 333 | ||
| 370 | static void iounmap_trace_core(volatile void __iomem *addr) | 334 | static void iounmap_trace_core(volatile void __iomem *addr) |
| 371 | { | 335 | { |
| 372 | struct mm_io_header_map event = { | 336 | struct mmiotrace_map map = { |
| 373 | .header = { | 337 | .phys = 0, |
| 374 | .type = MMIO_MAGIC | | 338 | .virt = (unsigned long)addr, |
| 375 | (MMIO_UNPROBE << MMIO_OPCODE_SHIFT), | 339 | .len = 0, |
| 376 | .sec = 0, | 340 | .opcode = MMIO_UNPROBE |
| 377 | .nsec = 0, | ||
| 378 | .pid = 0, | ||
| 379 | .data_len = sizeof(struct mm_io_map) | ||
| 380 | }, | ||
| 381 | .map = { | ||
| 382 | .phys = 0, | ||
| 383 | .addr = (unsigned long)addr, | ||
| 384 | .len = 0, | ||
| 385 | .pc = 0 | ||
| 386 | } | ||
| 387 | }; | 341 | }; |
| 388 | struct remap_trace *trace; | 342 | struct remap_trace *trace; |
| 389 | struct remap_trace *tmp; | 343 | struct remap_trace *tmp; |
| @@ -404,8 +358,8 @@ static void iounmap_trace_core(volatile void __iomem *addr) | |||
| 404 | break; | 358 | break; |
| 405 | } | 359 | } |
| 406 | } | 360 | } |
| 407 | mmio_trace_record(event.header.type, event.map.addr, | 361 | map.map_id = (found_trace) ? found_trace->id : -1; |
| 408 | found_trace ? found_trace->id : -1); | 362 | mmio_trace_mapping(&map); |
| 409 | 363 | ||
| 410 | not_enabled: | 364 | not_enabled: |
| 411 | spin_unlock_irq(&trace_lock); | 365 | spin_unlock_irq(&trace_lock); |
| @@ -448,10 +402,12 @@ static void clear_trace_list(void) | |||
| 448 | } | 402 | } |
| 449 | } | 403 | } |
| 450 | 404 | ||
| 405 | #if 0 /* XXX: out of order */ | ||
| 451 | static struct file_operations fops_marker = { | 406 | static struct file_operations fops_marker = { |
| 452 | .owner = THIS_MODULE, | 407 | .owner = THIS_MODULE, |
| 453 | .write = write_marker | 408 | .write = write_marker |
| 454 | }; | 409 | }; |
| 410 | #endif | ||
| 455 | 411 | ||
| 456 | void enable_mmiotrace(void) | 412 | void enable_mmiotrace(void) |
| 457 | { | 413 | { |
| @@ -464,9 +420,9 @@ void enable_mmiotrace(void) | |||
| 464 | #if 0 /* XXX: tracing does not support text entries */ | 420 | #if 0 /* XXX: tracing does not support text entries */ |
| 465 | marker_file = debugfs_create_file("marker", 0660, dir, NULL, | 421 | marker_file = debugfs_create_file("marker", 0660, dir, NULL, |
| 466 | &fops_marker); | 422 | &fops_marker); |
| 467 | #endif | ||
| 468 | if (!marker_file) | 423 | if (!marker_file) |
| 469 | pr_err(NAME "marker file creation failed.\n"); | 424 | pr_err(NAME "marker file creation failed.\n"); |
| 425 | #endif | ||
| 470 | 426 | ||
| 471 | if (nommiotrace) | 427 | if (nommiotrace) |
| 472 | pr_info(NAME "MMIO tracing disabled.\n"); | 428 | pr_info(NAME "MMIO tracing disabled.\n"); |
| @@ -502,17 +458,3 @@ void disable_mmiotrace(void) | |||
| 502 | out: | 458 | out: |
| 503 | mutex_unlock(&mmiotrace_mutex); | 459 | mutex_unlock(&mmiotrace_mutex); |
| 504 | } | 460 | } |
| 505 | |||
| 506 | int __init init_mmiotrace(void) | ||
| 507 | { | ||
| 508 | pr_debug(NAME "load...\n"); | ||
| 509 | if (n_subbufs < 2) | ||
| 510 | return -EINVAL; | ||
| 511 | |||
| 512 | dir = debugfs_create_dir(APP_DIR, NULL); | ||
| 513 | if (!dir) { | ||
| 514 | pr_err(NAME "Couldn't create relay app directory.\n"); | ||
| 515 | return -ENOMEM; | ||
| 516 | } | ||
| 517 | return 0; | ||
| 518 | } | ||
diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h index 579b3b06c90e..c88a9c197d22 100644 --- a/include/linux/mmiotrace.h +++ b/include/linux/mmiotrace.h | |||
| @@ -54,73 +54,38 @@ static inline void mmiotrace_iounmap(volatile void __iomem *addr) | |||
| 54 | } | 54 | } |
| 55 | #endif /* CONFIG_MMIOTRACE_HOOKS */ | 55 | #endif /* CONFIG_MMIOTRACE_HOOKS */ |
| 56 | 56 | ||
| 57 | /* in kernel/trace/trace_mmiotrace.c */ | 57 | enum mm_io_opcode { |
| 58 | extern int __init init_mmiotrace(void); | 58 | MMIO_READ = 0x1, /* struct mmiotrace_rw */ |
| 59 | extern void enable_mmiotrace(void); | 59 | MMIO_WRITE = 0x2, /* struct mmiotrace_rw */ |
| 60 | extern void disable_mmiotrace(void); | 60 | MMIO_PROBE = 0x3, /* struct mmiotrace_map */ |
| 61 | extern void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg); | 61 | MMIO_UNPROBE = 0x4, /* struct mmiotrace_map */ |
| 62 | |||
| 63 | #endif /* __KERNEL__ */ | ||
| 64 | |||
| 65 | |||
| 66 | /* | ||
| 67 | * If you change anything here, you must bump MMIO_VERSION. | ||
| 68 | * This is the relay data format for user space. | ||
| 69 | */ | ||
| 70 | #define MMIO_VERSION 0x04 | ||
| 71 | |||
| 72 | /* mm_io_header.type */ | ||
| 73 | #define MMIO_OPCODE_MASK 0xff | ||
| 74 | #define MMIO_OPCODE_SHIFT 0 | ||
| 75 | #define MMIO_WIDTH_MASK 0xff00 | ||
| 76 | #define MMIO_WIDTH_SHIFT 8 | ||
| 77 | #define MMIO_MAGIC (0x6f000000 | (MMIO_VERSION<<16)) | ||
| 78 | #define MMIO_MAGIC_MASK 0xffff0000 | ||
| 79 | |||
| 80 | enum mm_io_opcode { /* payload type: */ | ||
| 81 | MMIO_READ = 0x1, /* struct mm_io_rw */ | ||
| 82 | MMIO_WRITE = 0x2, /* struct mm_io_rw */ | ||
| 83 | MMIO_PROBE = 0x3, /* struct mm_io_map */ | ||
| 84 | MMIO_UNPROBE = 0x4, /* struct mm_io_map */ | ||
| 85 | MMIO_MARKER = 0x5, /* raw char data */ | 62 | MMIO_MARKER = 0x5, /* raw char data */ |
| 86 | MMIO_UNKNOWN_OP = 0x6, /* struct mm_io_rw */ | 63 | MMIO_UNKNOWN_OP = 0x6, /* struct mmiotrace_rw */ |
| 87 | }; | 64 | }; |
| 88 | 65 | ||
| 89 | struct mm_io_header { | 66 | struct mmiotrace_rw { |
| 90 | __u32 type; /* see MMIO_* macros above */ | 67 | unsigned long phys; /* PCI address of register */ |
| 91 | __u32 sec; /* timestamp */ | 68 | unsigned long value; |
| 92 | __u32 nsec; | 69 | unsigned long pc; /* optional program counter */ |
| 93 | __u32 pid; /* PID of the process, or 0 for kernel core */ | 70 | int map_id; |
| 94 | __u16 data_len; /* length of the following payload */ | 71 | unsigned char opcode; /* one of MMIO_{READ,WRITE,UNKNOWN_OP} */ |
| 72 | unsigned char width; /* size of register access in bytes */ | ||
| 95 | }; | 73 | }; |
| 96 | 74 | ||
| 97 | struct mm_io_rw { | 75 | struct mmiotrace_map { |
| 98 | __u64 address; /* virtual address of register */ | 76 | unsigned long phys; /* base address in PCI space */ |
| 99 | __u64 value; | 77 | unsigned long virt; /* base virtual address */ |
| 100 | __u64 pc; /* optional program counter */ | 78 | unsigned long len; /* mapping size */ |
| 79 | int map_id; | ||
| 80 | unsigned char opcode; /* MMIO_PROBE or MMIO_UNPROBE */ | ||
| 101 | }; | 81 | }; |
| 102 | 82 | ||
| 103 | struct mm_io_map { | 83 | /* in kernel/trace/trace_mmiotrace.c */ |
| 104 | __u64 phys; /* base address in PCI space */ | 84 | extern void enable_mmiotrace(void); |
| 105 | __u64 addr; /* base virtual address */ | 85 | extern void disable_mmiotrace(void); |
| 106 | __u64 len; /* mapping size */ | 86 | extern void mmio_trace_rw(struct mmiotrace_rw *rw); |
| 107 | __u64 pc; /* optional program counter */ | 87 | extern void mmio_trace_mapping(struct mmiotrace_map *map); |
| 108 | }; | ||
| 109 | |||
| 110 | |||
| 111 | /* | ||
| 112 | * These structures are used to allow a single relay_write() | ||
| 113 | * call to write a full packet. | ||
| 114 | */ | ||
| 115 | |||
| 116 | struct mm_io_header_rw { | ||
| 117 | struct mm_io_header header; | ||
| 118 | struct mm_io_rw rw; | ||
| 119 | } __attribute__((packed)); | ||
| 120 | 88 | ||
| 121 | struct mm_io_header_map { | 89 | #endif /* __KERNEL__ */ |
| 122 | struct mm_io_header header; | ||
| 123 | struct mm_io_map map; | ||
| 124 | } __attribute__((packed)); | ||
| 125 | 90 | ||
| 126 | #endif /* MMIOTRACE_H */ | 91 | #endif /* MMIOTRACE_H */ |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 3271916ff033..d14fe49e9638 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
| @@ -831,6 +831,40 @@ ftrace(struct trace_array *tr, struct trace_array_cpu *data, | |||
| 831 | trace_function(tr, data, ip, parent_ip, flags); | 831 | trace_function(tr, data, ip, parent_ip, flags); |
| 832 | } | 832 | } |
| 833 | 833 | ||
| 834 | #ifdef CONFIG_MMIOTRACE | ||
| 835 | void __trace_mmiotrace_rw(struct trace_array *tr, struct trace_array_cpu *data, | ||
| 836 | struct mmiotrace_rw *rw) | ||
| 837 | { | ||
| 838 | struct trace_entry *entry; | ||
| 839 | unsigned long irq_flags; | ||
| 840 | |||
| 841 | spin_lock_irqsave(&data->lock, irq_flags); | ||
| 842 | entry = tracing_get_trace_entry(tr, data); | ||
| 843 | tracing_generic_entry_update(entry, 0); | ||
| 844 | entry->type = TRACE_MMIO_RW; | ||
| 845 | entry->mmiorw = *rw; | ||
| 846 | spin_unlock_irqrestore(&data->lock, irq_flags); | ||
| 847 | |||
| 848 | trace_wake_up(); | ||
| 849 | } | ||
| 850 | |||
| 851 | void __trace_mmiotrace_map(struct trace_array *tr, struct trace_array_cpu *data, | ||
| 852 | struct mmiotrace_map *map) | ||
| 853 | { | ||
| 854 | struct trace_entry *entry; | ||
| 855 | unsigned long irq_flags; | ||
| 856 | |||
| 857 | spin_lock_irqsave(&data->lock, irq_flags); | ||
| 858 | entry = tracing_get_trace_entry(tr, data); | ||
| 859 | tracing_generic_entry_update(entry, 0); | ||
| 860 | entry->type = TRACE_MMIO_MAP; | ||
| 861 | entry->mmiomap = *map; | ||
| 862 | spin_unlock_irqrestore(&data->lock, irq_flags); | ||
| 863 | |||
| 864 | trace_wake_up(); | ||
| 865 | } | ||
| 866 | #endif | ||
| 867 | |||
| 834 | void __trace_stack(struct trace_array *tr, | 868 | void __trace_stack(struct trace_array *tr, |
| 835 | struct trace_array_cpu *data, | 869 | struct trace_array_cpu *data, |
| 836 | unsigned long flags, | 870 | unsigned long flags, |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index c460e85e94ed..0ef9ef74c806 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <asm/atomic.h> | 5 | #include <asm/atomic.h> |
| 6 | #include <linux/sched.h> | 6 | #include <linux/sched.h> |
| 7 | #include <linux/clocksource.h> | 7 | #include <linux/clocksource.h> |
| 8 | #include <linux/mmiotrace.h> | ||
| 8 | 9 | ||
| 9 | enum trace_type { | 10 | enum trace_type { |
| 10 | __TRACE_FIRST_TYPE = 0, | 11 | __TRACE_FIRST_TYPE = 0, |
| @@ -14,6 +15,8 @@ enum trace_type { | |||
| 14 | TRACE_WAKE, | 15 | TRACE_WAKE, |
| 15 | TRACE_STACK, | 16 | TRACE_STACK, |
| 16 | TRACE_SPECIAL, | 17 | TRACE_SPECIAL, |
| 18 | TRACE_MMIO_RW, | ||
| 19 | TRACE_MMIO_MAP, | ||
| 17 | 20 | ||
| 18 | __TRACE_LAST_TYPE | 21 | __TRACE_LAST_TYPE |
| 19 | }; | 22 | }; |
| @@ -75,6 +78,8 @@ struct trace_entry { | |||
| 75 | struct ctx_switch_entry ctx; | 78 | struct ctx_switch_entry ctx; |
| 76 | struct special_entry special; | 79 | struct special_entry special; |
| 77 | struct stack_entry stack; | 80 | struct stack_entry stack; |
| 81 | struct mmiotrace_rw mmiorw; | ||
| 82 | struct mmiotrace_map mmiomap; | ||
| 78 | }; | 83 | }; |
| 79 | }; | 84 | }; |
| 80 | 85 | ||
| @@ -255,6 +260,15 @@ extern unsigned long ftrace_update_tot_cnt; | |||
| 255 | extern int DYN_FTRACE_TEST_NAME(void); | 260 | extern int DYN_FTRACE_TEST_NAME(void); |
| 256 | #endif | 261 | #endif |
| 257 | 262 | ||
| 263 | #ifdef CONFIG_MMIOTRACE | ||
| 264 | extern void __trace_mmiotrace_rw(struct trace_array *tr, | ||
| 265 | struct trace_array_cpu *data, | ||
| 266 | struct mmiotrace_rw *rw); | ||
| 267 | extern void __trace_mmiotrace_map(struct trace_array *tr, | ||
| 268 | struct trace_array_cpu *data, | ||
| 269 | struct mmiotrace_map *map); | ||
| 270 | #endif | ||
| 271 | |||
| 258 | #ifdef CONFIG_FTRACE_STARTUP_TEST | 272 | #ifdef CONFIG_FTRACE_STARTUP_TEST |
| 259 | #ifdef CONFIG_FTRACE | 273 | #ifdef CONFIG_FTRACE |
| 260 | extern int trace_selftest_startup_function(struct tracer *trace, | 274 | extern int trace_selftest_startup_function(struct tracer *trace, |
diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index e4dd03cc5aa6..3a12b1ad0c63 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c | |||
| @@ -11,19 +11,26 @@ | |||
| 11 | 11 | ||
| 12 | #include "trace.h" | 12 | #include "trace.h" |
| 13 | 13 | ||
| 14 | extern void | ||
| 15 | __trace_special(void *__tr, void *__data, | ||
| 16 | unsigned long arg1, unsigned long arg2, unsigned long arg3); | ||
| 17 | |||
| 18 | static struct trace_array *mmio_trace_array; | 14 | static struct trace_array *mmio_trace_array; |
| 19 | 15 | ||
| 16 | static void mmio_reset_data(struct trace_array *tr) | ||
| 17 | { | ||
| 18 | int cpu; | ||
| 19 | |||
| 20 | tr->time_start = ftrace_now(tr->cpu); | ||
| 21 | |||
| 22 | for_each_online_cpu(cpu) | ||
| 23 | tracing_reset(tr->data[cpu]); | ||
| 24 | } | ||
| 20 | 25 | ||
| 21 | static void mmio_trace_init(struct trace_array *tr) | 26 | static void mmio_trace_init(struct trace_array *tr) |
| 22 | { | 27 | { |
| 23 | pr_debug("in %s\n", __func__); | 28 | pr_debug("in %s\n", __func__); |
| 24 | mmio_trace_array = tr; | 29 | mmio_trace_array = tr; |
| 25 | if (tr->ctrl) | 30 | if (tr->ctrl) { |
| 31 | mmio_reset_data(tr); | ||
| 26 | enable_mmiotrace(); | 32 | enable_mmiotrace(); |
| 33 | } | ||
| 27 | } | 34 | } |
| 28 | 35 | ||
| 29 | static void mmio_trace_reset(struct trace_array *tr) | 36 | static void mmio_trace_reset(struct trace_array *tr) |
| @@ -31,15 +38,110 @@ static void mmio_trace_reset(struct trace_array *tr) | |||
| 31 | pr_debug("in %s\n", __func__); | 38 | pr_debug("in %s\n", __func__); |
| 32 | if (tr->ctrl) | 39 | if (tr->ctrl) |
| 33 | disable_mmiotrace(); | 40 | disable_mmiotrace(); |
| 41 | mmio_reset_data(tr); | ||
| 42 | mmio_trace_array = NULL; | ||
| 34 | } | 43 | } |
| 35 | 44 | ||
| 36 | static void mmio_trace_ctrl_update(struct trace_array *tr) | 45 | static void mmio_trace_ctrl_update(struct trace_array *tr) |
| 37 | { | 46 | { |
| 38 | pr_debug("in %s\n", __func__); | 47 | pr_debug("in %s\n", __func__); |
| 39 | if (tr->ctrl) | 48 | if (tr->ctrl) { |
| 49 | mmio_reset_data(tr); | ||
| 40 | enable_mmiotrace(); | 50 | enable_mmiotrace(); |
| 41 | else | 51 | } else { |
| 42 | disable_mmiotrace(); | 52 | disable_mmiotrace(); |
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | /* XXX: This is not called for trace_pipe file! */ | ||
| 57 | void mmio_print_header(struct trace_iterator *iter) | ||
| 58 | { | ||
| 59 | struct trace_seq *s = &iter->seq; | ||
| 60 | trace_seq_printf(s, "VERSION broken 20070824\n"); | ||
| 61 | /* TODO: print /proc/bus/pci/devices contents as PCIDEV lines */ | ||
| 62 | } | ||
| 63 | |||
| 64 | static int mmio_print_rw(struct trace_iterator *iter) | ||
| 65 | { | ||
| 66 | struct trace_entry *entry = iter->ent; | ||
| 67 | struct mmiotrace_rw *rw = &entry->mmiorw; | ||
| 68 | struct trace_seq *s = &iter->seq; | ||
| 69 | unsigned long long t = ns2usecs(entry->t); | ||
| 70 | unsigned long usec_rem = do_div(t, 1000000ULL); | ||
| 71 | unsigned secs = (unsigned long)t; | ||
| 72 | int ret = 1; | ||
| 73 | |||
| 74 | switch (entry->mmiorw.opcode) { | ||
| 75 | case MMIO_READ: | ||
| 76 | ret = trace_seq_printf(s, | ||
| 77 | "R %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n", | ||
| 78 | rw->width, secs, usec_rem, rw->map_id, rw->phys, | ||
| 79 | rw->value, rw->pc, entry->pid); | ||
| 80 | break; | ||
| 81 | case MMIO_WRITE: | ||
| 82 | ret = trace_seq_printf(s, | ||
| 83 | "W %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n", | ||
| 84 | rw->width, secs, usec_rem, rw->map_id, rw->phys, | ||
| 85 | rw->value, rw->pc, entry->pid); | ||
| 86 | break; | ||
| 87 | case MMIO_UNKNOWN_OP: | ||
| 88 | ret = trace_seq_printf(s, | ||
| 89 | "UNKNOWN %lu.%06lu %d 0x%lx %02x,%02x,%02x 0x%lx %d\n", | ||
| 90 | secs, usec_rem, rw->map_id, rw->phys, | ||
| 91 | (rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff, | ||
| 92 | (rw->value >> 0) & 0xff, rw->pc, entry->pid); | ||
| 93 | break; | ||
| 94 | default: | ||
| 95 | ret = trace_seq_printf(s, "rw what?\n"); | ||
| 96 | break; | ||
| 97 | } | ||
| 98 | if (ret) | ||
| 99 | return 1; | ||
| 100 | return 0; | ||
| 101 | } | ||
| 102 | |||
| 103 | static int mmio_print_map(struct trace_iterator *iter) | ||
| 104 | { | ||
| 105 | struct trace_entry *entry = iter->ent; | ||
| 106 | struct mmiotrace_map *m = &entry->mmiomap; | ||
| 107 | struct trace_seq *s = &iter->seq; | ||
| 108 | unsigned long long t = ns2usecs(entry->t); | ||
| 109 | unsigned long usec_rem = do_div(t, 1000000ULL); | ||
| 110 | unsigned secs = (unsigned long)t; | ||
| 111 | int ret = 1; | ||
| 112 | |||
| 113 | switch (entry->mmiorw.opcode) { | ||
| 114 | case MMIO_PROBE: | ||
| 115 | ret = trace_seq_printf(s, | ||
| 116 | "MAP %lu.%06lu %d 0x%lx 0x%lx 0x%lx 0x%lx %d\n", | ||
| 117 | secs, usec_rem, m->map_id, m->phys, m->virt, m->len, | ||
| 118 | 0UL, entry->pid); | ||
| 119 | break; | ||
| 120 | case MMIO_UNPROBE: | ||
| 121 | ret = trace_seq_printf(s, | ||
| 122 | "UNMAP %lu.%06lu %d 0x%lx %d\n", | ||
| 123 | secs, usec_rem, m->map_id, 0UL, entry->pid); | ||
| 124 | break; | ||
| 125 | default: | ||
| 126 | ret = trace_seq_printf(s, "map what?\n"); | ||
| 127 | break; | ||
| 128 | } | ||
| 129 | if (ret) | ||
| 130 | return 1; | ||
| 131 | return 0; | ||
| 132 | } | ||
| 133 | |||
| 134 | /* return 0 to abort printing without consuming current entry in pipe mode */ | ||
| 135 | static int mmio_print_line(struct trace_iterator *iter) | ||
| 136 | { | ||
| 137 | switch (iter->ent->type) { | ||
| 138 | case TRACE_MMIO_RW: | ||
| 139 | return mmio_print_rw(iter); | ||
| 140 | case TRACE_MMIO_MAP: | ||
| 141 | return mmio_print_map(iter); | ||
| 142 | default: | ||
| 143 | return 1; /* ignore unknown entries */ | ||
| 144 | } | ||
| 43 | } | 145 | } |
| 44 | 146 | ||
| 45 | static struct tracer mmio_tracer __read_mostly = | 147 | static struct tracer mmio_tracer __read_mostly = |
| @@ -47,38 +149,31 @@ static struct tracer mmio_tracer __read_mostly = | |||
| 47 | .name = "mmiotrace", | 149 | .name = "mmiotrace", |
| 48 | .init = mmio_trace_init, | 150 | .init = mmio_trace_init, |
| 49 | .reset = mmio_trace_reset, | 151 | .reset = mmio_trace_reset, |
| 152 | .open = mmio_print_header, | ||
| 50 | .ctrl_update = mmio_trace_ctrl_update, | 153 | .ctrl_update = mmio_trace_ctrl_update, |
| 154 | .print_line = mmio_print_line, | ||
| 51 | }; | 155 | }; |
| 52 | 156 | ||
| 53 | __init static int init_mmio_trace(void) | 157 | __init static int init_mmio_trace(void) |
| 54 | { | 158 | { |
| 55 | int ret = init_mmiotrace(); | ||
| 56 | if (ret) | ||
| 57 | return ret; | ||
| 58 | return register_tracer(&mmio_tracer); | 159 | return register_tracer(&mmio_tracer); |
| 59 | } | 160 | } |
| 60 | device_initcall(init_mmio_trace); | 161 | device_initcall(init_mmio_trace); |
| 61 | 162 | ||
| 62 | void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg) | 163 | void mmio_trace_rw(struct mmiotrace_rw *rw) |
| 63 | { | 164 | { |
| 64 | struct trace_array *tr = mmio_trace_array; | 165 | struct trace_array *tr = mmio_trace_array; |
| 65 | struct trace_array_cpu *data = tr->data[smp_processor_id()]; | 166 | struct trace_array_cpu *data = tr->data[smp_processor_id()]; |
| 167 | __trace_mmiotrace_rw(tr, data, rw); | ||
| 168 | } | ||
| 66 | 169 | ||
| 67 | if (!current || current->pid == 0) { | 170 | void mmio_trace_mapping(struct mmiotrace_map *map) |
| 68 | /* | 171 | { |
| 69 | * XXX: This is a problem. We need to able to record, no | 172 | struct trace_array *tr = mmio_trace_array; |
| 70 | * matter what. tracing_generic_entry_update() would crash. | 173 | struct trace_array_cpu *data; |
| 71 | */ | 174 | |
| 72 | static unsigned limit; | 175 | preempt_disable(); |
| 73 | if (limit++ < 12) | 176 | data = tr->data[smp_processor_id()]; |
| 74 | pr_err("Error in %s: no current.\n", __func__); | 177 | __trace_mmiotrace_map(tr, data, map); |
| 75 | return; | 178 | preempt_enable(); |
| 76 | } | ||
| 77 | if (!tr || !data) { | ||
| 78 | static unsigned limit; | ||
| 79 | if (limit++ < 12) | ||
| 80 | pr_err("%s: no tr or data\n", __func__); | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | __trace_special(tr, data, type, addr, arg); | ||
| 84 | } | 179 | } |
