aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/ds.c56
-rw-r--r--arch/x86/kernel/ptrace.c221
-rw-r--r--include/asm-x86/ds.h7
-rw-r--r--include/asm-x86/ptrace-abi.h94
-rw-r--r--include/asm-x86/ptrace.h1
5 files changed, 237 insertions, 142 deletions
diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c
index e7855def97c3..6eb5d49a36bb 100644
--- a/arch/x86/kernel/ds.c
+++ b/arch/x86/kernel/ds.c
@@ -177,18 +177,20 @@ static inline void set_info_data(char *base, unsigned long value)
177} 177}
178 178
179 179
180int ds_allocate(void **dsp, size_t bts_size_in_records) 180int ds_allocate(void **dsp, size_t bts_size_in_bytes)
181{ 181{
182 size_t bts_size_in_bytes = 0; 182 size_t bts_size_in_records;
183 void *bts = 0; 183 void *bts;
184 void *ds = 0; 184 void *ds;
185 185
186 if (!ds_cfg.sizeof_ds || !ds_cfg.sizeof_bts) 186 if (!ds_cfg.sizeof_ds || !ds_cfg.sizeof_bts)
187 return -EOPNOTSUPP; 187 return -EOPNOTSUPP;
188 188
189 if (bts_size_in_records < 0) 189 if (bts_size_in_bytes < 0)
190 return -EINVAL; 190 return -EINVAL;
191 191
192 bts_size_in_records =
193 bts_size_in_bytes / ds_cfg.sizeof_bts;
192 bts_size_in_bytes = 194 bts_size_in_bytes =
193 bts_size_in_records * ds_cfg.sizeof_bts; 195 bts_size_in_records * ds_cfg.sizeof_bts;
194 196
@@ -233,9 +235,21 @@ int ds_get_bts_size(void *ds)
233 if (!ds_cfg.sizeof_ds || !ds_cfg.sizeof_bts) 235 if (!ds_cfg.sizeof_ds || !ds_cfg.sizeof_bts)
234 return -EOPNOTSUPP; 236 return -EOPNOTSUPP;
235 237
238 if (!ds)
239 return 0;
240
236 size_in_bytes = 241 size_in_bytes =
237 get_bts_absolute_maximum(ds) - 242 get_bts_absolute_maximum(ds) -
238 get_bts_buffer_base(ds); 243 get_bts_buffer_base(ds);
244 return size_in_bytes;
245}
246
247int ds_get_bts_end(void *ds)
248{
249 size_t size_in_bytes = ds_get_bts_size(ds);
250
251 if (size_in_bytes <= 0)
252 return size_in_bytes;
239 253
240 return size_in_bytes / ds_cfg.sizeof_bts; 254 return size_in_bytes / ds_cfg.sizeof_bts;
241} 255}
@@ -254,6 +268,38 @@ int ds_get_bts_index(void *ds)
254 return index_offset_in_bytes / ds_cfg.sizeof_bts; 268 return index_offset_in_bytes / ds_cfg.sizeof_bts;
255} 269}
256 270
271int ds_set_overflow(void *ds, int method)
272{
273 switch (method) {
274 case DS_O_SIGNAL:
275 return -EOPNOTSUPP;
276 case DS_O_WRAP:
277 return 0;
278 default:
279 return -EINVAL;
280 }
281}
282
283int ds_get_overflow(void *ds)
284{
285 return DS_O_WRAP;
286}
287
288int ds_clear(void *ds)
289{
290 int bts_size = ds_get_bts_size(ds);
291 void *bts_base;
292
293 if (bts_size <= 0)
294 return bts_size;
295
296 bts_base = get_bts_buffer_base(ds);
297 memset(bts_base, 0, bts_size);
298
299 set_bts_index(ds, bts_base);
300 return 0;
301}
302
257int ds_read_bts(void *ds, size_t index, struct bts_struct *out) 303int ds_read_bts(void *ds, size_t index, struct bts_struct *out)
258{ 304{
259 void *bts; 305 void *bts;
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 3e78c124e2d2..18972a305890 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -33,12 +33,6 @@
33 33
34 34
35/* 35/*
36 * The maximal size of a BTS buffer per traced task in number of BTS
37 * records.
38 */
39#define PTRACE_BTS_BUFFER_MAX 4000
40
41/*
42 * does not yet catch signals sent when the child dies. 36 * does not yet catch signals sent when the child dies.
43 * in exit.c or in signal.c. 37 * in exit.c or in signal.c.
44 */ 38 */
@@ -466,17 +460,12 @@ static int ptrace_set_debugreg(struct task_struct *child,
466 return 0; 460 return 0;
467} 461}
468 462
469static int ptrace_bts_max_buffer_size(void) 463static int ptrace_bts_get_size(struct task_struct *child)
470{
471 return PTRACE_BTS_BUFFER_MAX;
472}
473
474static int ptrace_bts_get_buffer_size(struct task_struct *child)
475{ 464{
476 if (!child->thread.ds_area_msr) 465 if (!child->thread.ds_area_msr)
477 return -ENXIO; 466 return -ENXIO;
478 467
479 return ds_get_bts_size((void *)child->thread.ds_area_msr); 468 return ds_get_bts_index((void *)child->thread.ds_area_msr);
480} 469}
481 470
482static int ptrace_bts_read_record(struct task_struct *child, 471static int ptrace_bts_read_record(struct task_struct *child,
@@ -485,7 +474,7 @@ static int ptrace_bts_read_record(struct task_struct *child,
485{ 474{
486 struct bts_struct ret; 475 struct bts_struct ret;
487 int retval; 476 int retval;
488 int bts_size; 477 int bts_end;
489 int bts_index; 478 int bts_index;
490 479
491 if (!child->thread.ds_area_msr) 480 if (!child->thread.ds_area_msr)
@@ -494,15 +483,15 @@ static int ptrace_bts_read_record(struct task_struct *child,
494 if (index < 0) 483 if (index < 0)
495 return -EINVAL; 484 return -EINVAL;
496 485
497 bts_size = ds_get_bts_size((void *)child->thread.ds_area_msr); 486 bts_end = ds_get_bts_end((void *)child->thread.ds_area_msr);
498 if (bts_size <= index) 487 if (bts_end <= index)
499 return -EINVAL; 488 return -EINVAL;
500 489
501 /* translate the ptrace bts index into the ds bts index */ 490 /* translate the ptrace bts index into the ds bts index */
502 bts_index = ds_get_bts_index((void *)child->thread.ds_area_msr); 491 bts_index = ds_get_bts_index((void *)child->thread.ds_area_msr);
503 bts_index -= (index + 1); 492 bts_index -= (index + 1);
504 if (bts_index < 0) 493 if (bts_index < 0)
505 bts_index += bts_size; 494 bts_index += bts_end;
506 495
507 retval = ds_read_bts((void *)child->thread.ds_area_msr, 496 retval = ds_read_bts((void *)child->thread.ds_area_msr,
508 bts_index, &ret); 497 bts_index, &ret);
@@ -530,19 +519,97 @@ static int ptrace_bts_write_record(struct task_struct *child,
530 return sizeof(*in); 519 return sizeof(*in);
531} 520}
532 521
533static int ptrace_bts_config(struct task_struct *child, 522static int ptrace_bts_clear(struct task_struct *child)
534 unsigned long options)
535{ 523{
536 unsigned long debugctl_mask = ds_debugctl_mask(); 524 if (!child->thread.ds_area_msr)
537 int retval; 525 return -ENXIO;
538 526
539 retval = ptrace_bts_get_buffer_size(child); 527 return ds_clear((void *)child->thread.ds_area_msr);
540 if (retval < 0) 528}
541 return retval; 529
542 if (retval == 0) 530static int ptrace_bts_drain(struct task_struct *child,
531 struct bts_struct __user *out)
532{
533 int end, i;
534 void *ds = (void *)child->thread.ds_area_msr;
535
536 if (!ds)
543 return -ENXIO; 537 return -ENXIO;
544 538
545 if (options & PTRACE_BTS_O_TRACE_TASK) { 539 end = ds_get_bts_index(ds);
540 if (end <= 0)
541 return end;
542
543 for (i = 0; i < end; i++, out++) {
544 struct bts_struct ret;
545 int retval;
546
547 retval = ds_read_bts(ds, i, &ret);
548 if (retval < 0)
549 return retval;
550
551 if (copy_to_user(out, &ret, sizeof(ret)))
552 return -EFAULT;
553 }
554
555 ds_clear(ds);
556
557 return i;
558}
559
560static int ptrace_bts_config(struct task_struct *child,
561 const struct ptrace_bts_config __user *ucfg)
562{
563 struct ptrace_bts_config cfg;
564 unsigned long debugctl_mask;
565 int bts_size, ret;
566 void *ds;
567
568 if (copy_from_user(&cfg, ucfg, sizeof(cfg)))
569 return -EFAULT;
570
571 bts_size = 0;
572 ds = (void *)child->thread.ds_area_msr;
573 if (ds) {
574 bts_size = ds_get_bts_size(ds);
575 if (bts_size < 0)
576 return bts_size;
577 }
578
579 if (bts_size != cfg.size) {
580 ret = ds_free((void **)&child->thread.ds_area_msr);
581 if (ret < 0)
582 return ret;
583
584 if (cfg.size > 0)
585 ret = ds_allocate((void **)&child->thread.ds_area_msr,
586 cfg.size);
587 ds = (void *)child->thread.ds_area_msr;
588 if (ds)
589 set_tsk_thread_flag(child, TIF_DS_AREA_MSR);
590 else
591 clear_tsk_thread_flag(child, TIF_DS_AREA_MSR);
592
593 if (ret < 0)
594 return ret;
595
596 bts_size = ds_get_bts_size(ds);
597 if (bts_size <= 0)
598 return bts_size;
599 }
600
601 if (ds) {
602 if (cfg.flags & PTRACE_BTS_O_SIGNAL) {
603 ret = ds_set_overflow(ds, DS_O_SIGNAL);
604 } else {
605 ret = ds_set_overflow(ds, DS_O_WRAP);
606 }
607 if (ret < 0)
608 return ret;
609 }
610
611 debugctl_mask = ds_debugctl_mask();
612 if (ds && (cfg.flags & PTRACE_BTS_O_TRACE)) {
546 child->thread.debugctlmsr |= debugctl_mask; 613 child->thread.debugctlmsr |= debugctl_mask;
547 set_tsk_thread_flag(child, TIF_DEBUGCTLMSR); 614 set_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
548 } else { 615 } else {
@@ -555,7 +622,7 @@ static int ptrace_bts_config(struct task_struct *child,
555 clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); 622 clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
556 } 623 }
557 624
558 if (options & PTRACE_BTS_O_TIMESTAMPS) 625 if (ds && (cfg.flags & PTRACE_BTS_O_SCHED))
559 set_tsk_thread_flag(child, TIF_BTS_TRACE_TS); 626 set_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
560 else 627 else
561 clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); 628 clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
@@ -563,59 +630,32 @@ static int ptrace_bts_config(struct task_struct *child,
563 return 0; 630 return 0;
564} 631}
565 632
566static int ptrace_bts_status(struct task_struct *child) 633static int ptrace_bts_status(struct task_struct *child,
634 struct ptrace_bts_config __user *ucfg)
567{ 635{
568 unsigned long debugctl_mask = ds_debugctl_mask(); 636 void *ds = (void *)child->thread.ds_area_msr;
569 int retval, status = 0; 637 struct ptrace_bts_config cfg;
570
571 retval = ptrace_bts_get_buffer_size(child);
572 if (retval < 0)
573 return retval;
574 if (retval == 0)
575 return -ENXIO;
576
577 if (ptrace_bts_get_buffer_size(child) <= 0)
578 return -ENXIO;
579 638
580 if (test_tsk_thread_flag(child, TIF_DEBUGCTLMSR) && 639 memset(&cfg, 0, sizeof(cfg));
581 child->thread.debugctlmsr & debugctl_mask)
582 status |= PTRACE_BTS_O_TRACE_TASK;
583 if (test_tsk_thread_flag(child, TIF_BTS_TRACE_TS))
584 status |= PTRACE_BTS_O_TIMESTAMPS;
585 640
586 return status; 641 if (ds) {
587} 642 cfg.size = ds_get_bts_size(ds);
588 643
589static int ptrace_bts_allocate_bts(struct task_struct *child, 644 if (ds_get_overflow(ds) == DS_O_SIGNAL)
590 int size_in_records) 645 cfg.flags |= PTRACE_BTS_O_SIGNAL;
591{
592 int retval = 0;
593 void *ds;
594 646
595 if (size_in_records < 0) 647 if (test_tsk_thread_flag(child, TIF_DEBUGCTLMSR) &&
596 return -EINVAL; 648 child->thread.debugctlmsr & ds_debugctl_mask())
649 cfg.flags |= PTRACE_BTS_O_TRACE;
597 650
598 if (size_in_records > ptrace_bts_max_buffer_size()) 651 if (test_tsk_thread_flag(child, TIF_BTS_TRACE_TS))
599 return -EINVAL; 652 cfg.flags |= PTRACE_BTS_O_SCHED;
600
601 if (size_in_records == 0) {
602 ptrace_bts_config(child, /* options = */ 0);
603 } else {
604 retval = ds_allocate(&ds, size_in_records);
605 if (retval)
606 return retval;
607 } 653 }
608 654
609 if (child->thread.ds_area_msr) 655 if (copy_to_user(ucfg, &cfg, sizeof(cfg)))
610 ds_free((void **)&child->thread.ds_area_msr); 656 return -EFAULT;
611
612 child->thread.ds_area_msr = (unsigned long)ds;
613 if (child->thread.ds_area_msr)
614 set_tsk_thread_flag(child, TIF_DS_AREA_MSR);
615 else
616 clear_tsk_thread_flag(child, TIF_DS_AREA_MSR);
617 657
618 return retval; 658 return sizeof(cfg);
619} 659}
620 660
621void ptrace_bts_take_timestamp(struct task_struct *tsk, 661void ptrace_bts_take_timestamp(struct task_struct *tsk,
@@ -626,9 +666,6 @@ void ptrace_bts_take_timestamp(struct task_struct *tsk,
626 .variant.jiffies = jiffies 666 .variant.jiffies = jiffies
627 }; 667 };
628 668
629 if (ptrace_bts_get_buffer_size(tsk) <= 0)
630 return;
631
632 ptrace_bts_write_record(tsk, &rec); 669 ptrace_bts_write_record(tsk, &rec);
633} 670}
634 671
@@ -808,30 +845,32 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
808 break; 845 break;
809#endif 846#endif
810 847
811 case PTRACE_BTS_MAX_BUFFER_SIZE: 848 case PTRACE_BTS_CONFIG:
812 ret = ptrace_bts_max_buffer_size(); 849 ret = ptrace_bts_config
850 (child, (struct ptrace_bts_config __user *)addr);
813 break; 851 break;
814 852
815 case PTRACE_BTS_ALLOCATE_BUFFER: 853 case PTRACE_BTS_STATUS:
816 ret = ptrace_bts_allocate_bts(child, data); 854 ret = ptrace_bts_status
855 (child, (struct ptrace_bts_config __user *)addr);
817 break; 856 break;
818 857
819 case PTRACE_BTS_GET_BUFFER_SIZE: 858 case PTRACE_BTS_SIZE:
820 ret = ptrace_bts_get_buffer_size(child); 859 ret = ptrace_bts_get_size(child);
821 break; 860 break;
822 861
823 case PTRACE_BTS_READ_RECORD: 862 case PTRACE_BTS_GET:
824 ret = ptrace_bts_read_record 863 ret = ptrace_bts_read_record
825 (child, data, 864 (child, data, (struct bts_struct __user *) addr);
826 (struct bts_struct __user *) addr);
827 break; 865 break;
828 866
829 case PTRACE_BTS_CONFIG: 867 case PTRACE_BTS_CLEAR:
830 ret = ptrace_bts_config(child, data); 868 ret = ptrace_bts_clear(child);
831 break; 869 break;
832 870
833 case PTRACE_BTS_STATUS: 871 case PTRACE_BTS_DRAIN:
834 ret = ptrace_bts_status(child); 872 ret = ptrace_bts_drain
873 (child, (struct bts_struct __user *) addr);
835 break; 874 break;
836 875
837 default: 876 default:
@@ -1017,12 +1056,12 @@ asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
1017 case PTRACE_SETOPTIONS: 1056 case PTRACE_SETOPTIONS:
1018 case PTRACE_SET_THREAD_AREA: 1057 case PTRACE_SET_THREAD_AREA:
1019 case PTRACE_GET_THREAD_AREA: 1058 case PTRACE_GET_THREAD_AREA:
1020 case PTRACE_BTS_MAX_BUFFER_SIZE:
1021 case PTRACE_BTS_ALLOCATE_BUFFER:
1022 case PTRACE_BTS_GET_BUFFER_SIZE:
1023 case PTRACE_BTS_READ_RECORD:
1024 case PTRACE_BTS_CONFIG: 1059 case PTRACE_BTS_CONFIG:
1025 case PTRACE_BTS_STATUS: 1060 case PTRACE_BTS_STATUS:
1061 case PTRACE_BTS_SIZE:
1062 case PTRACE_BTS_GET:
1063 case PTRACE_BTS_CLEAR:
1064 case PTRACE_BTS_DRAIN:
1026 return sys_ptrace(request, pid, addr, data); 1065 return sys_ptrace(request, pid, addr, data);
1027 1066
1028 default: 1067 default:
diff --git a/include/asm-x86/ds.h b/include/asm-x86/ds.h
index c9e15381dc7f..b84040abee68 100644
--- a/include/asm-x86/ds.h
+++ b/include/asm-x86/ds.h
@@ -52,11 +52,18 @@ struct bts_struct {
52 } variant; 52 } variant;
53}; 53};
54 54
55/* Overflow handling mechanisms */
56#define DS_O_SIGNAL 1 /* send overflow signal */
57#define DS_O_WRAP 2 /* wrap around */
55 58
56extern int ds_allocate(void **, size_t); 59extern int ds_allocate(void **, size_t);
57extern int ds_free(void **); 60extern int ds_free(void **);
58extern int ds_get_bts_size(void *); 61extern int ds_get_bts_size(void *);
62extern int ds_get_bts_end(void *);
59extern int ds_get_bts_index(void *); 63extern int ds_get_bts_index(void *);
64extern int ds_set_overflow(void *, int);
65extern int ds_get_overflow(void *);
66extern int ds_clear(void *);
60extern int ds_read_bts(void *, size_t, struct bts_struct *); 67extern int ds_read_bts(void *, size_t, struct bts_struct *);
61extern int ds_write_bts(void *, const struct bts_struct *); 68extern int ds_write_bts(void *, const struct bts_struct *);
62extern unsigned long ds_debugctl_mask(void); 69extern unsigned long ds_debugctl_mask(void);
diff --git a/include/asm-x86/ptrace-abi.h b/include/asm-x86/ptrace-abi.h
index b473ad45e9ca..cf2fe4633ee5 100644
--- a/include/asm-x86/ptrace-abi.h
+++ b/include/asm-x86/ptrace-abi.h
@@ -80,51 +80,53 @@
80 80
81#define PTRACE_SINGLEBLOCK 33 /* resume execution until next branch */ 81#define PTRACE_SINGLEBLOCK 33 /* resume execution until next branch */
82 82
83/* Return maximal BTS buffer size in number of records, 83/* configuration/status structure used in PTRACE_BTS_CONFIG and
84 if successuf; -1, otherwise. 84 PTRACE_BTS_STATUS commands.
85 EOPNOTSUPP...processor does not support bts tracing */ 85*/
86#define PTRACE_BTS_MAX_BUFFER_SIZE 40 86struct ptrace_bts_config {
87 87 /* requested or actual size of BTS buffer in bytes */
88/* Allocate new bts buffer (free old one, if exists) of size DATA bts records; 88 unsigned long size;
89 parameter ADDR is ignored. 89 /* bitmask of below flags */
90 Return 0, if successful; -1, otherwise. 90 unsigned long flags;
91 EOPNOTSUPP...processor does not support bts tracing 91};
92 EINVAL.......invalid size in records 92
93 ENOMEM.......out of memory */ 93#define PTRACE_BTS_O_TRACE 0x1 /* branch trace */
94#define PTRACE_BTS_ALLOCATE_BUFFER 41 94#define PTRACE_BTS_O_SCHED 0x2 /* scheduling events w/ jiffies */
95 95#define PTRACE_BTS_O_SIGNAL 0x4 /* send SIG? on buffer overflow
96/* Return the size of the bts buffer in number of bts records, 96 instead of wrapping around */
97 if successful; -1, otherwise. 97#define PTRACE_BTS_O_CUT_SIZE 0x8 /* cut requested size to max available
98 EOPNOTSUPP...processor does not support bts tracing 98 instead of failing */
99 ENXIO........no buffer allocated */ 99
100#define PTRACE_BTS_GET_BUFFER_SIZE 42 100#define PTRACE_BTS_CONFIG 40
101 101/* Configure branch trace recording.
102/* Read the DATA'th bts record into a ptrace_bts_record buffer 102 DATA is ignored, ADDR points to a struct ptrace_bts_config.
103 provided in ADDR. 103 A new buffer is allocated, iff the size changes.
104 Records are ordered from newest to oldest. 104*/
105 Return 0, if successful; -1, otherwise 105#define PTRACE_BTS_STATUS 41
106 EOPNOTSUPP...processor does not support bts tracing 106/* Return the current configuration.
107 ENXIO........no buffer allocated 107 DATA is ignored, ADDR points to a struct ptrace_bts_config
108 EINVAL.......invalid index */ 108 that will contain the result.
109#define PTRACE_BTS_READ_RECORD 43 109*/
110 110#define PTRACE_BTS_SIZE 42
111/* Configure last branch trace; the configuration is given as a bit-mask of 111/* Return the number of available BTS records.
112 PTRACE_BTS_O_* options in DATA; parameter ADDR is ignored. 112 DATA and ADDR are ignored.
113 Return 0, if successful; -1, otherwise 113*/
114 EOPNOTSUPP...processor does not support bts tracing 114#define PTRACE_BTS_GET 43
115 ENXIO........no buffer allocated */ 115/* Get a single BTS record.
116#define PTRACE_BTS_CONFIG 44 116 DATA defines the index into the BTS array, where 0 is the newest
117 117 entry, and higher indices refer to older entries.
118/* Return the configuration as bit-mask of PTRACE_BTS_O_* options 118 ADDR is pointing to struct bts_struct (see asm/ds.h).
119 if successful; -1, otherwise. 119*/
120 EOPNOTSUPP...processor does not support bts tracing 120#define PTRACE_BTS_CLEAR 44
121 ENXIO........no buffer allocated */ 121/* Clear the BTS buffer.
122#define PTRACE_BTS_STATUS 45 122 DATA and ADDR are ignored.
123 123*/
124/* Trace configuration options */ 124#define PTRACE_BTS_DRAIN 45
125/* Collect last branch trace */ 125/* Read all available BTS records and clear the buffer.
126#define PTRACE_BTS_O_TRACE_TASK 0x1 126 DATA is ignored. ADDR points to an array of struct bts_struct of
127/* Take timestamps when the task arrives and departs */ 127 suitable size.
128#define PTRACE_BTS_O_TIMESTAMPS 0x2 128 BTS records are read from oldest to newest.
129 Returns number of BTS records drained.
130*/
129 131
130#endif 132#endif
diff --git a/include/asm-x86/ptrace.h b/include/asm-x86/ptrace.h
index a9a1bab1451a..61946fe8c085 100644
--- a/include/asm-x86/ptrace.h
+++ b/include/asm-x86/ptrace.h
@@ -9,6 +9,7 @@
9 9
10#ifdef __KERNEL__ 10#ifdef __KERNEL__
11 11
12/* the DS BTS struct is used for ptrace as well */
12#include <asm/ds.h> 13#include <asm/ds.h>
13 14
14struct task_struct; 15struct task_struct;