|
1 | 1 | /* Copyright (c) 2011-2015 PLUMgrid, http://plumgrid.com
|
| 2 | + * Copyright (c) 2016 Facebook |
2 | 3 | *
|
3 | 4 | * This program is free software; you can redistribute it and/or
|
4 | 5 | * modify it under the terms of version 2 of the GNU General Public
|
|
8 | 9 | #include <linux/types.h>
|
9 | 10 | #include <linux/slab.h>
|
10 | 11 | #include <linux/bpf.h>
|
| 12 | +#include <linux/bpf_perf_event.h> |
11 | 13 | #include <linux/filter.h>
|
12 | 14 | #include <linux/uaccess.h>
|
13 | 15 | #include <linux/ctype.h>
|
@@ -552,10 +554,69 @@ static struct bpf_prog_type_list tracepoint_tl = {
|
552 | 554 | .type = BPF_PROG_TYPE_TRACEPOINT,
|
553 | 555 | };
|
554 | 556 |
|
| 557 | +static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type, |
| 558 | + enum bpf_reg_type *reg_type) |
| 559 | +{ |
| 560 | + if (off < 0 || off >= sizeof(struct bpf_perf_event_data)) |
| 561 | + return false; |
| 562 | + if (type != BPF_READ) |
| 563 | + return false; |
| 564 | + if (off % size != 0) |
| 565 | + return false; |
| 566 | + if (off == offsetof(struct bpf_perf_event_data, sample_period)) { |
| 567 | + if (size != sizeof(u64)) |
| 568 | + return false; |
| 569 | + } else { |
| 570 | + if (size != sizeof(long)) |
| 571 | + return false; |
| 572 | + } |
| 573 | + return true; |
| 574 | +} |
| 575 | + |
| 576 | +static u32 pe_prog_convert_ctx_access(enum bpf_access_type type, int dst_reg, |
| 577 | + int src_reg, int ctx_off, |
| 578 | + struct bpf_insn *insn_buf, |
| 579 | + struct bpf_prog *prog) |
| 580 | +{ |
| 581 | + struct bpf_insn *insn = insn_buf; |
| 582 | + |
| 583 | + switch (ctx_off) { |
| 584 | + case offsetof(struct bpf_perf_event_data, sample_period): |
| 585 | + BUILD_BUG_ON(FIELD_SIZEOF(struct perf_sample_data, period) != sizeof(u64)); |
| 586 | + *insn++ = BPF_LDX_MEM(bytes_to_bpf_size(FIELD_SIZEOF(struct bpf_perf_event_data_kern, data)), |
| 587 | + dst_reg, src_reg, |
| 588 | + offsetof(struct bpf_perf_event_data_kern, data)); |
| 589 | + *insn++ = BPF_LDX_MEM(BPF_DW, dst_reg, dst_reg, |
| 590 | + offsetof(struct perf_sample_data, period)); |
| 591 | + break; |
| 592 | + default: |
| 593 | + *insn++ = BPF_LDX_MEM(bytes_to_bpf_size(FIELD_SIZEOF(struct bpf_perf_event_data_kern, regs)), |
| 594 | + dst_reg, src_reg, |
| 595 | + offsetof(struct bpf_perf_event_data_kern, regs)); |
| 596 | + *insn++ = BPF_LDX_MEM(bytes_to_bpf_size(sizeof(long)), |
| 597 | + dst_reg, dst_reg, ctx_off); |
| 598 | + break; |
| 599 | + } |
| 600 | + |
| 601 | + return insn - insn_buf; |
| 602 | +} |
| 603 | + |
| 604 | +static const struct bpf_verifier_ops perf_event_prog_ops = { |
| 605 | + .get_func_proto = tp_prog_func_proto, |
| 606 | + .is_valid_access = pe_prog_is_valid_access, |
| 607 | + .convert_ctx_access = pe_prog_convert_ctx_access, |
| 608 | +}; |
| 609 | + |
| 610 | +static struct bpf_prog_type_list perf_event_tl = { |
| 611 | + .ops = &perf_event_prog_ops, |
| 612 | + .type = BPF_PROG_TYPE_PERF_EVENT, |
| 613 | +}; |
| 614 | + |
555 | 615 | static int __init register_kprobe_prog_ops(void)
|
556 | 616 | {
|
557 | 617 | bpf_register_prog_type(&kprobe_tl);
|
558 | 618 | bpf_register_prog_type(&tracepoint_tl);
|
| 619 | + bpf_register_prog_type(&perf_event_tl); |
559 | 620 | return 0;
|
560 | 621 | }
|
561 | 622 | late_initcall(register_kprobe_prog_ops);
|
0 commit comments