diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/Kconfig.debug | 2 | ||||
-rw-r--r-- | arch/x86/kernel/mmiotrace/mmio-mod.c | 140 |
2 files changed, 42 insertions, 100 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 | } | ||