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 | } |