From 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Tue, 4 Aug 2015 12:17:53 -0700 Subject: Add the rt linux 4.1.3-rt3 as base Import the rt linux 4.1.3-rt3 as OPNFV kvm base. It's from git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git linux-4.1.y-rt and the base is: commit 0917f823c59692d751951bf5ea699a2d1e2f26a2 Author: Sebastian Andrzej Siewior Date: Sat Jul 25 12:13:34 2015 +0200 Prepare v4.1.3-rt3 Signed-off-by: Sebastian Andrzej Siewior We lose all the git history this way and it's not good. We should apply another opnfv project repo in future. Change-Id: I87543d81c9df70d99c5001fbdf646b202c19f423 Signed-off-by: Yunhong Jiang --- kernel/tools/Makefile | 119 + kernel/tools/build/Build.include | 81 + kernel/tools/build/Documentation/Build.txt | 139 + kernel/tools/build/Makefile.build | 130 + kernel/tools/build/Makefile.feature | 171 + kernel/tools/build/feature/.gitignore | 3 + kernel/tools/build/feature/Makefile | 164 + kernel/tools/build/feature/test-all.c | 148 + kernel/tools/build/feature/test-backtrace.c | 13 + kernel/tools/build/feature/test-bionic.c | 6 + kernel/tools/build/feature/test-compile.c | 4 + kernel/tools/build/feature/test-cplus-demangle.c | 14 + kernel/tools/build/feature/test-dwarf.c | 10 + kernel/tools/build/feature/test-fortify-source.c | 6 + kernel/tools/build/feature/test-glibc.c | 8 + kernel/tools/build/feature/test-gtk2-infobar.c | 11 + kernel/tools/build/feature/test-gtk2.c | 10 + kernel/tools/build/feature/test-hello.c | 6 + kernel/tools/build/feature/test-libaudit.c | 10 + kernel/tools/build/feature/test-libbabeltrace.c | 9 + kernel/tools/build/feature/test-libbfd.c | 15 + .../tools/build/feature/test-libdw-dwarf-unwind.c | 13 + .../tools/build/feature/test-libelf-getphdrnum.c | 8 + kernel/tools/build/feature/test-libelf-mmap.c | 8 + kernel/tools/build/feature/test-libelf.c | 8 + kernel/tools/build/feature/test-libnuma.c | 9 + kernel/tools/build/feature/test-libperl.c | 9 + .../tools/build/feature/test-libpython-version.c | 10 + kernel/tools/build/feature/test-libpython.c | 8 + kernel/tools/build/feature/test-libslang.c | 6 + .../build/feature/test-libunwind-debug-frame.c | 16 + kernel/tools/build/feature/test-libunwind.c | 27 + kernel/tools/build/feature/test-lzma.c | 10 + .../feature/test-pthread-attr-setaffinity-np.c | 17 + .../tools/build/feature/test-stackprotector-all.c | 6 + .../build/feature/test-sync-compare-and-swap.c | 14 + kernel/tools/build/feature/test-timerfd.c | 18 + kernel/tools/build/feature/test-zlib.c | 9 + kernel/tools/build/tests/ex/Build | 8 + kernel/tools/build/tests/ex/Makefile | 23 + kernel/tools/build/tests/ex/a.c | 5 + kernel/tools/build/tests/ex/arch/Build | 2 + kernel/tools/build/tests/ex/arch/e.c | 5 + kernel/tools/build/tests/ex/arch/f.c | 5 + kernel/tools/build/tests/ex/b.c | 5 + kernel/tools/build/tests/ex/c.c | 5 + kernel/tools/build/tests/ex/d.c | 5 + kernel/tools/build/tests/ex/empty/Build | 0 kernel/tools/build/tests/ex/ex.c | 19 + kernel/tools/build/tests/run.sh | 42 + kernel/tools/cgroup/.gitignore | 1 + kernel/tools/cgroup/Makefile | 11 + kernel/tools/cgroup/cgroup_event_listener.c | 82 + kernel/tools/firewire/Makefile | 19 + kernel/tools/firewire/decode-fcp.c | 213 + kernel/tools/firewire/list.h | 62 + kernel/tools/firewire/nosy-dump.c | 1035 +++ kernel/tools/firewire/nosy-dump.h | 173 + kernel/tools/hv/Makefile | 13 + kernel/tools/hv/hv_fcopy_daemon.c | 221 + kernel/tools/hv/hv_get_dhcp_info.sh | 28 + kernel/tools/hv/hv_get_dns_info.sh | 13 + kernel/tools/hv/hv_kvp_daemon.c | 1783 ++++++ kernel/tools/hv/hv_set_ifconfig.sh | 64 + kernel/tools/hv/hv_vss_daemon.c | 342 + kernel/tools/iio/Makefile | 16 + kernel/tools/iio/generic_buffer.c | 359 ++ kernel/tools/iio/iio_event_monitor.c | 308 + kernel/tools/iio/iio_utils.c | 669 ++ kernel/tools/iio/iio_utils.h | 71 + kernel/tools/iio/lsiio.c | 158 + kernel/tools/include/asm-generic/bitops.h | 29 + kernel/tools/include/asm-generic/bitops/__ffs.h | 43 + kernel/tools/include/asm-generic/bitops/__fls.h | 1 + .../include/asm-generic/bitops/arch_hweight.h | 1 + kernel/tools/include/asm-generic/bitops/atomic.h | 22 + .../include/asm-generic/bitops/const_hweight.h | 1 + kernel/tools/include/asm-generic/bitops/find.h | 33 + kernel/tools/include/asm-generic/bitops/fls.h | 1 + kernel/tools/include/asm-generic/bitops/fls64.h | 1 + kernel/tools/include/asm-generic/bitops/hweight.h | 7 + kernel/tools/include/asm/bug.h | 25 + kernel/tools/include/linux/bitops.h | 58 + kernel/tools/include/linux/compiler.h | 40 + kernel/tools/include/linux/export.h | 10 + kernel/tools/include/linux/hash.h | 5 + kernel/tools/include/linux/log2.h | 185 + kernel/tools/include/linux/types.h | 75 + kernel/tools/include/tools/be_byteshift.h | 70 + kernel/tools/include/tools/endian.h | 56 + kernel/tools/include/tools/le_byteshift.h | 70 + kernel/tools/lguest/.gitignore | 1 + kernel/tools/lguest/Makefile | 13 + kernel/tools/lguest/extract | 58 + kernel/tools/lguest/lguest.c | 3410 ++++++++++ kernel/tools/lguest/lguest.txt | 125 + kernel/tools/lib/api/Build | 2 + kernel/tools/lib/api/Makefile | 43 + kernel/tools/lib/api/fd/Build | 1 + kernel/tools/lib/api/fd/array.c | 127 + kernel/tools/lib/api/fd/array.h | 46 + kernel/tools/lib/api/fs/Build | 4 + kernel/tools/lib/api/fs/debugfs.c | 118 + kernel/tools/lib/api/fs/debugfs.h | 23 + kernel/tools/lib/api/fs/findfs.c | 63 + kernel/tools/lib/api/fs/findfs.h | 23 + kernel/tools/lib/api/fs/fs.c | 199 + kernel/tools/lib/api/fs/fs.h | 17 + kernel/tools/lib/api/fs/tracefs.c | 78 + kernel/tools/lib/api/fs/tracefs.h | 21 + kernel/tools/lib/lockdep/.gitignore | 1 + kernel/tools/lib/lockdep/Build | 1 + kernel/tools/lib/lockdep/Makefile | 158 + kernel/tools/lib/lockdep/common.c | 33 + .../tools/lib/lockdep/include/liblockdep/common.h | 50 + .../tools/lib/lockdep/include/liblockdep/mutex.h | 70 + .../tools/lib/lockdep/include/liblockdep/rwlock.h | 86 + kernel/tools/lib/lockdep/lockdep | 3 + kernel/tools/lib/lockdep/lockdep.c | 2 + kernel/tools/lib/lockdep/lockdep_internals.h | 1 + kernel/tools/lib/lockdep/lockdep_states.h | 1 + kernel/tools/lib/lockdep/preload.c | 445 ++ kernel/tools/lib/lockdep/rbtree.c | 1 + kernel/tools/lib/lockdep/run_tests.sh | 27 + kernel/tools/lib/lockdep/tests/AA.c | 13 + kernel/tools/lib/lockdep/tests/ABBA.c | 13 + kernel/tools/lib/lockdep/tests/ABBCCA.c | 15 + kernel/tools/lib/lockdep/tests/ABBCCDDA.c | 17 + kernel/tools/lib/lockdep/tests/ABCABC.c | 15 + kernel/tools/lib/lockdep/tests/ABCDBCDA.c | 17 + kernel/tools/lib/lockdep/tests/ABCDBDDA.c | 17 + kernel/tools/lib/lockdep/tests/WW.c | 13 + kernel/tools/lib/lockdep/tests/common.h | 12 + kernel/tools/lib/lockdep/tests/unlock_balance.c | 12 + kernel/tools/lib/lockdep/uinclude/asm/hash.h | 6 + kernel/tools/lib/lockdep/uinclude/asm/hweight.h | 3 + kernel/tools/lib/lockdep/uinclude/asm/sections.h | 3 + kernel/tools/lib/lockdep/uinclude/linux/bitops.h | 3 + kernel/tools/lib/lockdep/uinclude/linux/compiler.h | 7 + .../tools/lib/lockdep/uinclude/linux/debug_locks.h | 12 + kernel/tools/lib/lockdep/uinclude/linux/delay.h | 3 + kernel/tools/lib/lockdep/uinclude/linux/ftrace.h | 3 + kernel/tools/lib/lockdep/uinclude/linux/gfp.h | 3 + kernel/tools/lib/lockdep/uinclude/linux/hardirq.h | 11 + kernel/tools/lib/lockdep/uinclude/linux/hash.h | 1 + .../tools/lib/lockdep/uinclude/linux/interrupt.h | 3 + kernel/tools/lib/lockdep/uinclude/linux/irqflags.h | 38 + kernel/tools/lib/lockdep/uinclude/linux/kallsyms.h | 32 + .../tools/lib/lockdep/uinclude/linux/kern_levels.h | 25 + kernel/tools/lib/lockdep/uinclude/linux/kernel.h | 47 + .../tools/lib/lockdep/uinclude/linux/kmemcheck.h | 8 + kernel/tools/lib/lockdep/uinclude/linux/linkage.h | 3 + kernel/tools/lib/lockdep/uinclude/linux/list.h | 1 + kernel/tools/lib/lockdep/uinclude/linux/lockdep.h | 58 + kernel/tools/lib/lockdep/uinclude/linux/module.h | 6 + kernel/tools/lib/lockdep/uinclude/linux/mutex.h | 3 + kernel/tools/lib/lockdep/uinclude/linux/poison.h | 1 + kernel/tools/lib/lockdep/uinclude/linux/prefetch.h | 6 + kernel/tools/lib/lockdep/uinclude/linux/proc_fs.h | 3 + kernel/tools/lib/lockdep/uinclude/linux/rbtree.h | 1 + .../lib/lockdep/uinclude/linux/rbtree_augmented.h | 2 + kernel/tools/lib/lockdep/uinclude/linux/rcu.h | 21 + kernel/tools/lib/lockdep/uinclude/linux/seq_file.h | 3 + kernel/tools/lib/lockdep/uinclude/linux/spinlock.h | 25 + .../tools/lib/lockdep/uinclude/linux/stacktrace.h | 32 + .../tools/lib/lockdep/uinclude/linux/stringify.h | 7 + .../tools/lib/lockdep/uinclude/trace/events/lock.h | 3 + kernel/tools/lib/symbol/kallsyms.c | 58 + kernel/tools/lib/symbol/kallsyms.h | 24 + kernel/tools/lib/traceevent/.gitignore | 1 + kernel/tools/lib/traceevent/Build | 17 + kernel/tools/lib/traceevent/Makefile | 261 + kernel/tools/lib/traceevent/event-parse.c | 6572 ++++++++++++++++++++ kernel/tools/lib/traceevent/event-parse.h | 964 +++ kernel/tools/lib/traceevent/event-plugin.c | 464 ++ kernel/tools/lib/traceevent/event-utils.h | 81 + kernel/tools/lib/traceevent/kbuffer-parse.c | 742 +++ kernel/tools/lib/traceevent/kbuffer.h | 68 + kernel/tools/lib/traceevent/parse-filter.c | 2434 ++++++++ kernel/tools/lib/traceevent/parse-utils.c | 85 + kernel/tools/lib/traceevent/plugin_cfg80211.c | 29 + kernel/tools/lib/traceevent/plugin_function.c | 194 + kernel/tools/lib/traceevent/plugin_hrtimer.c | 88 + kernel/tools/lib/traceevent/plugin_jbd2.c | 75 + kernel/tools/lib/traceevent/plugin_kmem.c | 94 + kernel/tools/lib/traceevent/plugin_kvm.c | 513 ++ kernel/tools/lib/traceevent/plugin_mac80211.c | 102 + kernel/tools/lib/traceevent/plugin_sched_switch.c | 160 + kernel/tools/lib/traceevent/plugin_scsi.c | 432 ++ kernel/tools/lib/traceevent/plugin_xen.c | 136 + kernel/tools/lib/traceevent/trace-seq.c | 254 + kernel/tools/lib/util/find_next_bit.c | 89 + kernel/tools/net/Makefile | 34 + kernel/tools/net/bpf_asm.c | 52 + kernel/tools/net/bpf_dbg.c | 1395 +++++ kernel/tools/net/bpf_exp.l | 146 + kernel/tools/net/bpf_exp.y | 780 +++ kernel/tools/net/bpf_jit_disasm.c | 199 + kernel/tools/nfsd/inject_fault.sh | 49 + kernel/tools/perf/.gitignore | 30 + kernel/tools/perf/Build | 44 + kernel/tools/perf/CREDITS | 30 + kernel/tools/perf/Documentation/Build.txt | 49 + kernel/tools/perf/Documentation/Makefile | 343 + kernel/tools/perf/Documentation/android.txt | 78 + kernel/tools/perf/Documentation/asciidoc.conf | 91 + kernel/tools/perf/Documentation/examples.txt | 225 + kernel/tools/perf/Documentation/jit-interface.txt | 15 + kernel/tools/perf/Documentation/manpage-1.72.xsl | 14 + kernel/tools/perf/Documentation/manpage-base.xsl | 35 + .../perf/Documentation/manpage-bold-literal.xsl | 17 + kernel/tools/perf/Documentation/manpage-normal.xsl | 13 + .../perf/Documentation/manpage-suppress-sp.xsl | 21 + kernel/tools/perf/Documentation/perf-annotate.txt | 101 + kernel/tools/perf/Documentation/perf-archive.txt | 22 + kernel/tools/perf/Documentation/perf-bench.txt | 218 + .../perf/Documentation/perf-buildid-cache.txt | 63 + .../tools/perf/Documentation/perf-buildid-list.txt | 43 + kernel/tools/perf/Documentation/perf-data.txt | 40 + kernel/tools/perf/Documentation/perf-diff.txt | 214 + kernel/tools/perf/Documentation/perf-evlist.txt | 38 + kernel/tools/perf/Documentation/perf-help.txt | 38 + kernel/tools/perf/Documentation/perf-inject.txt | 49 + kernel/tools/perf/Documentation/perf-kmem.txt | 57 + kernel/tools/perf/Documentation/perf-kvm.txt | 158 + kernel/tools/perf/Documentation/perf-list.txt | 142 + kernel/tools/perf/Documentation/perf-lock.txt | 66 + kernel/tools/perf/Documentation/perf-mem.txt | 53 + kernel/tools/perf/Documentation/perf-probe.txt | 222 + kernel/tools/perf/Documentation/perf-record.txt | 262 + kernel/tools/perf/Documentation/perf-report.txt | 328 + kernel/tools/perf/Documentation/perf-sched.txt | 55 + .../tools/perf/Documentation/perf-script-perl.txt | 216 + .../perf/Documentation/perf-script-python.txt | 620 ++ kernel/tools/perf/Documentation/perf-script.txt | 227 + kernel/tools/perf/Documentation/perf-stat.txt | 177 + kernel/tools/perf/Documentation/perf-test.txt | 32 + kernel/tools/perf/Documentation/perf-timechart.txt | 126 + kernel/tools/perf/Documentation/perf-top.txt | 240 + kernel/tools/perf/Documentation/perf-trace.txt | 164 + kernel/tools/perf/Documentation/perf.txt | 41 + kernel/tools/perf/Documentation/perfconfig.example | 29 + kernel/tools/perf/MANIFEST | 63 + kernel/tools/perf/Makefile | 90 + kernel/tools/perf/Makefile.perf | 552 ++ kernel/tools/perf/arch/Build | 2 + kernel/tools/perf/arch/arm/Build | 2 + kernel/tools/perf/arch/arm/Makefile | 3 + kernel/tools/perf/arch/arm/include/perf_regs.h | 59 + kernel/tools/perf/arch/arm/tests/Build | 2 + kernel/tools/perf/arch/arm/tests/dwarf-unwind.c | 61 + kernel/tools/perf/arch/arm/tests/regs_load.S | 58 + kernel/tools/perf/arch/arm/util/Build | 4 + kernel/tools/perf/arch/arm/util/dwarf-regs.c | 64 + kernel/tools/perf/arch/arm/util/unwind-libdw.c | 36 + kernel/tools/perf/arch/arm/util/unwind-libunwind.c | 49 + kernel/tools/perf/arch/arm64/Build | 1 + kernel/tools/perf/arch/arm64/Makefile | 3 + kernel/tools/perf/arch/arm64/include/perf_regs.h | 90 + kernel/tools/perf/arch/arm64/util/Build | 2 + kernel/tools/perf/arch/arm64/util/dwarf-regs.c | 80 + .../tools/perf/arch/arm64/util/unwind-libunwind.c | 83 + kernel/tools/perf/arch/common.c | 220 + kernel/tools/perf/arch/common.h | 10 + kernel/tools/perf/arch/powerpc/Build | 1 + kernel/tools/perf/arch/powerpc/Makefile | 3 + kernel/tools/perf/arch/powerpc/util/Build | 4 + kernel/tools/perf/arch/powerpc/util/dwarf-regs.c | 88 + kernel/tools/perf/arch/powerpc/util/header.c | 34 + .../perf/arch/powerpc/util/skip-callchain-idx.c | 280 + kernel/tools/perf/arch/s390/Build | 1 + kernel/tools/perf/arch/s390/Makefile | 4 + kernel/tools/perf/arch/s390/util/Build | 4 + kernel/tools/perf/arch/s390/util/dwarf-regs.c | 22 + kernel/tools/perf/arch/s390/util/header.c | 28 + kernel/tools/perf/arch/s390/util/kvm-stat.c | 105 + kernel/tools/perf/arch/sh/Build | 1 + kernel/tools/perf/arch/sh/Makefile | 3 + kernel/tools/perf/arch/sh/util/Build | 1 + kernel/tools/perf/arch/sh/util/dwarf-regs.c | 55 + kernel/tools/perf/arch/sparc/Build | 1 + kernel/tools/perf/arch/sparc/Makefile | 3 + kernel/tools/perf/arch/sparc/util/Build | 1 + kernel/tools/perf/arch/sparc/util/dwarf-regs.c | 43 + kernel/tools/perf/arch/x86/Build | 2 + kernel/tools/perf/arch/x86/Makefile | 4 + kernel/tools/perf/arch/x86/include/perf_regs.h | 86 + kernel/tools/perf/arch/x86/tests/Build | 2 + kernel/tools/perf/arch/x86/tests/dwarf-unwind.c | 61 + kernel/tools/perf/arch/x86/tests/regs_load.S | 98 + kernel/tools/perf/arch/x86/util/Build | 8 + kernel/tools/perf/arch/x86/util/dwarf-regs.c | 75 + kernel/tools/perf/arch/x86/util/header.c | 59 + kernel/tools/perf/arch/x86/util/kvm-stat.c | 156 + kernel/tools/perf/arch/x86/util/tsc.c | 48 + kernel/tools/perf/arch/x86/util/tsc.h | 17 + kernel/tools/perf/arch/x86/util/unwind-libdw.c | 51 + kernel/tools/perf/arch/x86/util/unwind-libunwind.c | 112 + kernel/tools/perf/bench/Build | 11 + kernel/tools/perf/bench/bench.h | 48 + kernel/tools/perf/bench/futex-hash.c | 215 + kernel/tools/perf/bench/futex-requeue.c | 212 + kernel/tools/perf/bench/futex-wake.c | 198 + kernel/tools/perf/bench/futex.h | 84 + kernel/tools/perf/bench/mem-memcpy-arch.h | 12 + .../tools/perf/bench/mem-memcpy-x86-64-asm-def.h | 12 + kernel/tools/perf/bench/mem-memcpy-x86-64-asm.S | 10 + kernel/tools/perf/bench/mem-memcpy.c | 434 ++ kernel/tools/perf/bench/mem-memset-arch.h | 12 + .../tools/perf/bench/mem-memset-x86-64-asm-def.h | 12 + kernel/tools/perf/bench/mem-memset-x86-64-asm.S | 11 + kernel/tools/perf/bench/numa.c | 1752 ++++++ kernel/tools/perf/bench/sched-messaging.c | 331 + kernel/tools/perf/bench/sched-pipe.c | 184 + kernel/tools/perf/builtin-annotate.c | 386 ++ kernel/tools/perf/builtin-bench.c | 289 + kernel/tools/perf/builtin-buildid-cache.c | 448 ++ kernel/tools/perf/builtin-buildid-list.c | 111 + kernel/tools/perf/builtin-data.c | 123 + kernel/tools/perf/builtin-diff.c | 1285 ++++ kernel/tools/perf/builtin-evlist.c | 69 + kernel/tools/perf/builtin-help.c | 503 ++ kernel/tools/perf/builtin-inject.c | 482 ++ kernel/tools/perf/builtin-kmem.c | 1199 ++++ kernel/tools/perf/builtin-kvm.c | 1581 +++++ kernel/tools/perf/builtin-list.c | 82 + kernel/tools/perf/builtin-lock.c | 1021 +++ kernel/tools/perf/builtin-mem.c | 326 + kernel/tools/perf/builtin-probe.c | 526 ++ kernel/tools/perf/builtin-record.c | 1015 +++ kernel/tools/perf/builtin-report.c | 877 +++ kernel/tools/perf/builtin-sched.c | 1808 ++++++ kernel/tools/perf/builtin-script.c | 1844 ++++++ kernel/tools/perf/builtin-stat.c | 1935 ++++++ kernel/tools/perf/builtin-timechart.c | 2011 ++++++ kernel/tools/perf/builtin-top.c | 1273 ++++ kernel/tools/perf/builtin-trace.c | 2797 +++++++++ kernel/tools/perf/builtin.h | 43 + kernel/tools/perf/command-list.txt | 28 + kernel/tools/perf/config/Makefile | 710 +++ kernel/tools/perf/config/Makefile.arch | 18 + kernel/tools/perf/config/utilities.mak | 179 + kernel/tools/perf/design.txt | 462 ++ kernel/tools/perf/perf-archive.sh | 47 + kernel/tools/perf/perf-completion.sh | 258 + kernel/tools/perf/perf-read-vdso.c | 34 + kernel/tools/perf/perf-sys.h | 190 + kernel/tools/perf/perf-with-kcore.sh | 259 + kernel/tools/perf/perf.c | 610 ++ kernel/tools/perf/perf.h | 72 + kernel/tools/perf/python/twatch.py | 41 + kernel/tools/perf/scripts/Build | 2 + .../tools/perf/scripts/perl/Perf-Trace-Util/Build | 3 + .../perf/scripts/perl/Perf-Trace-Util/Context.c | 138 + .../perf/scripts/perl/Perf-Trace-Util/Context.xs | 42 + .../perf/scripts/perl/Perf-Trace-Util/Makefile.PL | 17 + .../tools/perf/scripts/perl/Perf-Trace-Util/README | 59 + .../perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm | 55 + .../perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm | 192 + .../perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm | 94 + .../perf/scripts/perl/Perf-Trace-Util/typemap | 1 + .../perf/scripts/perl/bin/check-perf-trace-record | 2 + .../perf/scripts/perl/bin/failed-syscalls-record | 3 + .../perf/scripts/perl/bin/failed-syscalls-report | 10 + .../tools/perf/scripts/perl/bin/rw-by-file-record | 3 + .../tools/perf/scripts/perl/bin/rw-by-file-report | 10 + .../tools/perf/scripts/perl/bin/rw-by-pid-record | 2 + .../tools/perf/scripts/perl/bin/rw-by-pid-report | 3 + kernel/tools/perf/scripts/perl/bin/rwtop-record | 2 + kernel/tools/perf/scripts/perl/bin/rwtop-report | 20 + .../perf/scripts/perl/bin/wakeup-latency-record | 6 + .../perf/scripts/perl/bin/wakeup-latency-report | 3 + kernel/tools/perf/scripts/perl/check-perf-trace.pl | 106 + kernel/tools/perf/scripts/perl/failed-syscalls.pl | 47 + kernel/tools/perf/scripts/perl/rw-by-file.pl | 106 + kernel/tools/perf/scripts/perl/rw-by-pid.pl | 184 + kernel/tools/perf/scripts/perl/rwtop.pl | 203 + kernel/tools/perf/scripts/perl/wakeup-latency.pl | 107 + .../perf/scripts/python/Perf-Trace-Util/Build | 3 + .../perf/scripts/python/Perf-Trace-Util/Context.c | 88 + .../python/Perf-Trace-Util/lib/Perf/Trace/Core.py | 122 + .../Perf-Trace-Util/lib/Perf/Trace/EventClass.py | 94 + .../Perf-Trace-Util/lib/Perf/Trace/SchedGui.py | 184 + .../python/Perf-Trace-Util/lib/Perf/Trace/Util.py | 86 + .../python/bin/event_analyzing_sample-record | 8 + .../python/bin/event_analyzing_sample-report | 3 + .../scripts/python/bin/export-to-postgresql-record | 8 + .../scripts/python/bin/export-to-postgresql-report | 29 + .../python/bin/failed-syscalls-by-pid-record | 3 + .../python/bin/failed-syscalls-by-pid-report | 10 + .../scripts/python/bin/futex-contention-record | 2 + .../scripts/python/bin/futex-contention-report | 4 + .../perf/scripts/python/bin/net_dropmonitor-record | 2 + .../perf/scripts/python/bin/net_dropmonitor-report | 4 + .../perf/scripts/python/bin/netdev-times-record | 8 + .../perf/scripts/python/bin/netdev-times-report | 5 + .../perf/scripts/python/bin/sched-migration-record | 2 + .../perf/scripts/python/bin/sched-migration-report | 3 + kernel/tools/perf/scripts/python/bin/sctop-record | 3 + kernel/tools/perf/scripts/python/bin/sctop-report | 24 + .../python/bin/syscall-counts-by-pid-record | 3 + .../python/bin/syscall-counts-by-pid-report | 10 + .../perf/scripts/python/bin/syscall-counts-record | 3 + .../perf/scripts/python/bin/syscall-counts-report | 10 + .../tools/perf/scripts/python/check-perf-trace.py | 82 + .../perf/scripts/python/event_analyzing_sample.py | 189 + .../perf/scripts/python/export-to-postgresql.py | 444 ++ .../perf/scripts/python/failed-syscalls-by-pid.py | 78 + .../tools/perf/scripts/python/futex-contention.py | 50 + .../tools/perf/scripts/python/net_dropmonitor.py | 75 + kernel/tools/perf/scripts/python/netdev-times.py | 464 ++ .../tools/perf/scripts/python/sched-migration.py | 460 ++ kernel/tools/perf/scripts/python/sctop.py | 80 + .../perf/scripts/python/syscall-counts-by-pid.py | 74 + kernel/tools/perf/scripts/python/syscall-counts.py | 64 + kernel/tools/perf/tests/Build | 43 + kernel/tools/perf/tests/attr.c | 176 + kernel/tools/perf/tests/attr.py | 331 + kernel/tools/perf/tests/attr/README | 64 + kernel/tools/perf/tests/attr/base-record | 41 + kernel/tools/perf/tests/attr/base-stat | 41 + kernel/tools/perf/tests/attr/test-record-C0 | 13 + kernel/tools/perf/tests/attr/test-record-basic | 5 + .../tools/perf/tests/attr/test-record-branch-any | 8 + .../perf/tests/attr/test-record-branch-filter-any | 8 + .../tests/attr/test-record-branch-filter-any_call | 8 + .../tests/attr/test-record-branch-filter-any_ret | 8 + .../perf/tests/attr/test-record-branch-filter-hv | 8 + .../tests/attr/test-record-branch-filter-ind_call | 8 + .../perf/tests/attr/test-record-branch-filter-k | 8 + .../perf/tests/attr/test-record-branch-filter-u | 8 + kernel/tools/perf/tests/attr/test-record-count | 8 + kernel/tools/perf/tests/attr/test-record-data | 11 + kernel/tools/perf/tests/attr/test-record-freq | 6 + .../perf/tests/attr/test-record-graph-default | 6 + .../tools/perf/tests/attr/test-record-graph-dwarf | 10 + kernel/tools/perf/tests/attr/test-record-graph-fp | 6 + kernel/tools/perf/tests/attr/test-record-group | 20 + .../perf/tests/attr/test-record-group-sampling | 36 + kernel/tools/perf/tests/attr/test-record-group1 | 21 + kernel/tools/perf/tests/attr/test-record-no-delay | 9 + .../tools/perf/tests/attr/test-record-no-inherit | 7 + .../tools/perf/tests/attr/test-record-no-samples | 6 + kernel/tools/perf/tests/attr/test-record-period | 7 + kernel/tools/perf/tests/attr/test-record-raw | 7 + kernel/tools/perf/tests/attr/test-stat-C0 | 9 + kernel/tools/perf/tests/attr/test-stat-basic | 6 + kernel/tools/perf/tests/attr/test-stat-default | 64 + kernel/tools/perf/tests/attr/test-stat-detailed-1 | 101 + kernel/tools/perf/tests/attr/test-stat-detailed-2 | 155 + kernel/tools/perf/tests/attr/test-stat-detailed-3 | 173 + kernel/tools/perf/tests/attr/test-stat-group | 15 + kernel/tools/perf/tests/attr/test-stat-group1 | 15 + kernel/tools/perf/tests/attr/test-stat-no-inherit | 7 + kernel/tools/perf/tests/bp_signal.c | 194 + kernel/tools/perf/tests/bp_signal_overflow.c | 134 + kernel/tools/perf/tests/builtin-test.c | 331 + kernel/tools/perf/tests/code-reading.c | 578 ++ kernel/tools/perf/tests/dso-data.c | 365 ++ kernel/tools/perf/tests/dwarf-unwind.c | 179 + kernel/tools/perf/tests/evsel-roundtrip-name.c | 115 + kernel/tools/perf/tests/evsel-tp-sched.c | 82 + kernel/tools/perf/tests/fdarray.c | 174 + kernel/tools/perf/tests/hists_common.c | 209 + kernel/tools/perf/tests/hists_common.h | 75 + kernel/tools/perf/tests/hists_cumulate.c | 726 +++ kernel/tools/perf/tests/hists_filter.c | 290 + kernel/tools/perf/tests/hists_link.c | 346 ++ kernel/tools/perf/tests/hists_output.c | 621 ++ kernel/tools/perf/tests/keep-tracking.c | 152 + kernel/tools/perf/tests/kmod-path.c | 73 + kernel/tools/perf/tests/make | 232 + kernel/tools/perf/tests/mmap-basic.c | 149 + kernel/tools/perf/tests/mmap-thread-lookup.c | 233 + kernel/tools/perf/tests/open-syscall-all-cpus.c | 115 + kernel/tools/perf/tests/open-syscall-tp-fields.c | 121 + kernel/tools/perf/tests/open-syscall.c | 61 + kernel/tools/perf/tests/parse-events.c | 1734 ++++++ kernel/tools/perf/tests/parse-no-sample-id-all.c | 109 + kernel/tools/perf/tests/perf-record.c | 314 + kernel/tools/perf/tests/perf-targz-src-pkg | 21 + kernel/tools/perf/tests/perf-time-to-tsc.c | 162 + kernel/tools/perf/tests/pmu.c | 173 + kernel/tools/perf/tests/python-use.c | 23 + kernel/tools/perf/tests/rdpmc.c | 177 + kernel/tools/perf/tests/sample-parsing.c | 346 ++ kernel/tools/perf/tests/sw-clock.c | 124 + kernel/tools/perf/tests/switch-tracking.c | 572 ++ kernel/tools/perf/tests/task-exit.c | 120 + kernel/tools/perf/tests/tests.h | 64 + kernel/tools/perf/tests/thread-mg-share.c | 91 + kernel/tools/perf/tests/vmlinux-kallsyms.c | 242 + kernel/tools/perf/ui/Build | 14 + kernel/tools/perf/ui/browser.c | 722 +++ kernel/tools/perf/ui/browser.h | 75 + kernel/tools/perf/ui/browsers/Build | 10 + kernel/tools/perf/ui/browsers/annotate.c | 1039 ++++ kernel/tools/perf/ui/browsers/header.c | 128 + kernel/tools/perf/ui/browsers/hists.c | 2042 ++++++ kernel/tools/perf/ui/browsers/map.c | 130 + kernel/tools/perf/ui/browsers/map.h | 6 + kernel/tools/perf/ui/browsers/scripts.c | 187 + kernel/tools/perf/ui/gtk/Build | 9 + kernel/tools/perf/ui/gtk/annotate.c | 252 + kernel/tools/perf/ui/gtk/browser.c | 87 + kernel/tools/perf/ui/gtk/gtk.h | 67 + kernel/tools/perf/ui/gtk/helpline.c | 57 + kernel/tools/perf/ui/gtk/hists.c | 356 ++ kernel/tools/perf/ui/gtk/progress.c | 59 + kernel/tools/perf/ui/gtk/setup.c | 23 + kernel/tools/perf/ui/gtk/util.c | 112 + kernel/tools/perf/ui/helpline.c | 73 + kernel/tools/perf/ui/helpline.h | 29 + kernel/tools/perf/ui/hist.c | 693 +++ kernel/tools/perf/ui/keysyms.h | 28 + kernel/tools/perf/ui/libslang.h | 29 + kernel/tools/perf/ui/progress.c | 38 + kernel/tools/perf/ui/progress.h | 23 + kernel/tools/perf/ui/setup.c | 107 + kernel/tools/perf/ui/stdio/hist.c | 511 ++ kernel/tools/perf/ui/tui/Build | 4 + kernel/tools/perf/ui/tui/helpline.c | 61 + kernel/tools/perf/ui/tui/progress.c | 44 + kernel/tools/perf/ui/tui/setup.c | 174 + kernel/tools/perf/ui/tui/tui.h | 6 + kernel/tools/perf/ui/tui/util.c | 256 + kernel/tools/perf/ui/ui.h | 29 + kernel/tools/perf/ui/util.c | 84 + kernel/tools/perf/ui/util.h | 21 + kernel/tools/perf/util/Build | 145 + kernel/tools/perf/util/PERF-VERSION-GEN | 50 + kernel/tools/perf/util/abspath.c | 37 + kernel/tools/perf/util/alias.c | 76 + kernel/tools/perf/util/annotate.c | 1479 +++++ kernel/tools/perf/util/annotate.h | 172 + kernel/tools/perf/util/bitmap.c | 31 + kernel/tools/perf/util/build-id.c | 538 ++ kernel/tools/perf/util/build-id.h | 34 + kernel/tools/perf/util/cache.h | 80 + kernel/tools/perf/util/callchain.c | 881 +++ kernel/tools/perf/util/callchain.h | 204 + kernel/tools/perf/util/cgroup.c | 177 + kernel/tools/perf/util/cgroup.h | 17 + kernel/tools/perf/util/cloexec.c | 92 + kernel/tools/perf/util/cloexec.h | 12 + kernel/tools/perf/util/color.c | 227 + kernel/tools/perf/util/color.h | 46 + kernel/tools/perf/util/comm.c | 125 + kernel/tools/perf/util/comm.h | 27 + kernel/tools/perf/util/config.c | 564 ++ kernel/tools/perf/util/cpumap.c | 479 ++ kernel/tools/perf/util/cpumap.h | 84 + kernel/tools/perf/util/ctype.c | 39 + kernel/tools/perf/util/data-convert-bt.c | 857 +++ kernel/tools/perf/util/data-convert-bt.h | 8 + kernel/tools/perf/util/data.c | 138 + kernel/tools/perf/util/data.h | 50 + kernel/tools/perf/util/db-export.c | 428 ++ kernel/tools/perf/util/db-export.h | 106 + kernel/tools/perf/util/debug.c | 189 + kernel/tools/perf/util/debug.h | 56 + kernel/tools/perf/util/dso.c | 1172 ++++ kernel/tools/perf/util/dso.h | 325 + kernel/tools/perf/util/dwarf-aux.c | 905 +++ kernel/tools/perf/util/dwarf-aux.h | 121 + kernel/tools/perf/util/environment.c | 9 + kernel/tools/perf/util/event.c | 978 +++ kernel/tools/perf/util/event.h | 395 ++ kernel/tools/perf/util/evlist.c | 1570 +++++ kernel/tools/perf/util/evlist.h | 284 + kernel/tools/perf/util/evsel.c | 2190 +++++++ kernel/tools/perf/util/evsel.h | 369 ++ kernel/tools/perf/util/exec_cmd.c | 148 + kernel/tools/perf/util/exec_cmd.h | 12 + kernel/tools/perf/util/find-vdso-map.c | 30 + kernel/tools/perf/util/generate-cmdlist.sh | 39 + kernel/tools/perf/util/header.c | 2781 +++++++++ kernel/tools/perf/util/header.h | 159 + kernel/tools/perf/util/help.c | 339 + kernel/tools/perf/util/help.h | 29 + kernel/tools/perf/util/hist.c | 1482 +++++ kernel/tools/perf/util/hist.h | 358 ++ .../tools/perf/util/include/asm/alternative-asm.h | 9 + kernel/tools/perf/util/include/asm/asm-offsets.h | 1 + kernel/tools/perf/util/include/asm/byteorder.h | 2 + kernel/tools/perf/util/include/asm/cpufeature.h | 9 + kernel/tools/perf/util/include/asm/dwarf2.h | 13 + kernel/tools/perf/util/include/asm/swab.h | 1 + kernel/tools/perf/util/include/asm/system.h | 1 + kernel/tools/perf/util/include/asm/uaccess.h | 14 + kernel/tools/perf/util/include/asm/unistd_32.h | 1 + kernel/tools/perf/util/include/asm/unistd_64.h | 1 + kernel/tools/perf/util/include/dwarf-regs.h | 8 + kernel/tools/perf/util/include/linux/bitmap.h | 66 + kernel/tools/perf/util/include/linux/const.h | 1 + kernel/tools/perf/util/include/linux/ctype.h | 1 + kernel/tools/perf/util/include/linux/kernel.h | 107 + kernel/tools/perf/util/include/linux/linkage.h | 13 + kernel/tools/perf/util/include/linux/list.h | 29 + kernel/tools/perf/util/include/linux/poison.h | 1 + kernel/tools/perf/util/include/linux/rbtree.h | 2 + .../perf/util/include/linux/rbtree_augmented.h | 2 + kernel/tools/perf/util/include/linux/string.h | 3 + kernel/tools/perf/util/intlist.c | 146 + kernel/tools/perf/util/intlist.h | 77 + kernel/tools/perf/util/kvm-stat.h | 140 + kernel/tools/perf/util/levenshtein.c | 84 + kernel/tools/perf/util/levenshtein.h | 8 + kernel/tools/perf/util/lzma.c | 95 + kernel/tools/perf/util/machine.c | 1847 ++++++ kernel/tools/perf/util/machine.h | 227 + kernel/tools/perf/util/map.c | 800 +++ kernel/tools/perf/util/map.h | 235 + kernel/tools/perf/util/ordered-events.c | 307 + kernel/tools/perf/util/ordered-events.h | 64 + kernel/tools/perf/util/pager.c | 100 + kernel/tools/perf/util/parse-events.c | 1537 +++++ kernel/tools/perf/util/parse-events.h | 136 + kernel/tools/perf/util/parse-events.l | 247 + kernel/tools/perf/util/parse-events.y | 526 ++ kernel/tools/perf/util/parse-options.c | 757 +++ kernel/tools/perf/util/parse-options.h | 220 + kernel/tools/perf/util/path.c | 161 + kernel/tools/perf/util/perf_regs.c | 27 + kernel/tools/perf/util/perf_regs.h | 29 + kernel/tools/perf/util/pmu.c | 992 +++ kernel/tools/perf/util/pmu.h | 79 + kernel/tools/perf/util/pmu.l | 43 + kernel/tools/perf/util/pmu.y | 92 + kernel/tools/perf/util/probe-event.c | 2847 +++++++++ kernel/tools/perf/util/probe-event.h | 142 + kernel/tools/perf/util/probe-finder.c | 1695 +++++ kernel/tools/perf/util/probe-finder.h | 116 + kernel/tools/perf/util/pstack.c | 76 + kernel/tools/perf/util/pstack.h | 14 + kernel/tools/perf/util/python-ext-sources | 21 + kernel/tools/perf/util/python.c | 1074 ++++ kernel/tools/perf/util/quote.c | 54 + kernel/tools/perf/util/quote.h | 29 + kernel/tools/perf/util/rblist.c | 128 + kernel/tools/perf/util/rblist.h | 48 + kernel/tools/perf/util/record.c | 243 + kernel/tools/perf/util/run-command.c | 219 + kernel/tools/perf/util/run-command.h | 58 + kernel/tools/perf/util/scripting-engines/Build | 6 + .../perf/util/scripting-engines/trace-event-perl.c | 632 ++ .../util/scripting-engines/trace-event-python.c | 1209 ++++ kernel/tools/perf/util/session.c | 1818 ++++++ kernel/tools/perf/util/session.h | 133 + kernel/tools/perf/util/setup.py | 48 + kernel/tools/perf/util/sigchain.c | 52 + kernel/tools/perf/util/sigchain.h | 10 + kernel/tools/perf/util/sort.c | 1861 ++++++ kernel/tools/perf/util/sort.h | 222 + kernel/tools/perf/util/srcline.c | 308 + kernel/tools/perf/util/stat.c | 63 + kernel/tools/perf/util/stat.h | 25 + kernel/tools/perf/util/strbuf.c | 134 + kernel/tools/perf/util/strbuf.h | 92 + kernel/tools/perf/util/strfilter.c | 199 + kernel/tools/perf/util/strfilter.h | 48 + kernel/tools/perf/util/string.c | 359 ++ kernel/tools/perf/util/strlist.c | 173 + kernel/tools/perf/util/strlist.h | 79 + kernel/tools/perf/util/svghelper.c | 808 +++ kernel/tools/perf/util/svghelper.h | 37 + kernel/tools/perf/util/symbol-elf.c | 1774 ++++++ kernel/tools/perf/util/symbol-minimal.c | 377 ++ kernel/tools/perf/util/symbol.c | 2001 ++++++ kernel/tools/perf/util/symbol.h | 306 + kernel/tools/perf/util/target.c | 155 + kernel/tools/perf/util/target.h | 79 + kernel/tools/perf/util/thread-stack.c | 747 +++ kernel/tools/perf/util/thread-stack.h | 111 + kernel/tools/perf/util/thread.c | 232 + kernel/tools/perf/util/thread.h | 117 + kernel/tools/perf/util/thread_map.c | 299 + kernel/tools/perf/util/thread_map.h | 30 + kernel/tools/perf/util/tool.h | 52 + kernel/tools/perf/util/top.c | 117 + kernel/tools/perf/util/top.h | 47 + kernel/tools/perf/util/trace-event-info.c | 595 ++ kernel/tools/perf/util/trace-event-parse.c | 269 + kernel/tools/perf/util/trace-event-read.c | 444 ++ kernel/tools/perf/util/trace-event-scripting.c | 177 + kernel/tools/perf/util/trace-event.c | 82 + kernel/tools/perf/util/trace-event.h | 93 + kernel/tools/perf/util/tsc.c | 30 + kernel/tools/perf/util/tsc.h | 12 + kernel/tools/perf/util/unwind-libdw.c | 211 + kernel/tools/perf/util/unwind-libdw.h | 21 + kernel/tools/perf/util/unwind-libunwind.c | 653 ++ kernel/tools/perf/util/unwind.h | 55 + kernel/tools/perf/util/usage.c | 84 + kernel/tools/perf/util/util.c | 617 ++ kernel/tools/perf/util/util.h | 336 + kernel/tools/perf/util/values.c | 232 + kernel/tools/perf/util/values.h | 27 + kernel/tools/perf/util/vdso.c | 321 + kernel/tools/perf/util/vdso.h | 29 + kernel/tools/perf/util/wrapper.c | 41 + kernel/tools/perf/util/xyarray.c | 20 + kernel/tools/perf/util/xyarray.h | 20 + kernel/tools/perf/util/zlib.c | 78 + kernel/tools/power/acpi/Makefile | 161 + kernel/tools/power/acpi/common/cmfsize.c | 103 + kernel/tools/power/acpi/common/getopt.c | 237 + kernel/tools/power/acpi/man/acpidump.8 | 120 + .../acpi/os_specific/service_layers/oslibcfs.c | 214 + .../acpi/os_specific/service_layers/oslinuxtbl.c | 1363 ++++ .../acpi/os_specific/service_layers/osunixdir.c | 204 + .../acpi/os_specific/service_layers/osunixmap.c | 151 + .../acpi/os_specific/service_layers/osunixxf.c | 1319 ++++ kernel/tools/power/acpi/tools/acpidump/acpidump.h | 129 + kernel/tools/power/acpi/tools/acpidump/apdump.c | 429 ++ kernel/tools/power/acpi/tools/acpidump/apfiles.c | 244 + kernel/tools/power/acpi/tools/acpidump/apmain.c | 371 ++ kernel/tools/power/acpi/tools/ec/Makefile | 22 + kernel/tools/power/acpi/tools/ec/ec_access.c | 238 + kernel/tools/power/cpupower/.gitignore | 29 + kernel/tools/power/cpupower/Makefile | 310 + kernel/tools/power/cpupower/README | 47 + kernel/tools/power/cpupower/ToDo | 10 + kernel/tools/power/cpupower/bench/Makefile | 36 + kernel/tools/power/cpupower/bench/README-BENCH | 124 + kernel/tools/power/cpupower/bench/benchmark.c | 194 + kernel/tools/power/cpupower/bench/benchmark.h | 29 + kernel/tools/power/cpupower/bench/config.h | 36 + .../power/cpupower/bench/cpufreq-bench_plot.sh | 104 + .../power/cpupower/bench/cpufreq-bench_script.sh | 101 + kernel/tools/power/cpupower/bench/example.cfg | 11 + kernel/tools/power/cpupower/bench/main.c | 202 + kernel/tools/power/cpupower/bench/parse.c | 228 + kernel/tools/power/cpupower/bench/parse.h | 53 + kernel/tools/power/cpupower/bench/system.c | 191 + kernel/tools/power/cpupower/bench/system.h | 29 + kernel/tools/power/cpupower/debug/i386/Makefile | 41 + .../power/cpupower/debug/i386/centrino-decode.c | 113 + kernel/tools/power/cpupower/debug/i386/dump_psb.c | 196 + .../tools/power/cpupower/debug/i386/intel_gsic.c | 78 + .../power/cpupower/debug/i386/powernow-k8-decode.c | 96 + kernel/tools/power/cpupower/debug/kernel/Makefile | 23 + .../power/cpupower/debug/kernel/cpufreq-test_tsc.c | 110 + kernel/tools/power/cpupower/debug/x86_64/Makefile | 30 + kernel/tools/power/cpupower/lib/cpufreq.c | 208 + kernel/tools/power/cpupower/lib/cpufreq.h | 223 + kernel/tools/power/cpupower/lib/sysfs.c | 672 ++ kernel/tools/power/cpupower/lib/sysfs.h | 31 + .../power/cpupower/man/cpupower-frequency-info.1 | 77 + .../power/cpupower/man/cpupower-frequency-set.1 | 52 + .../tools/power/cpupower/man/cpupower-idle-info.1 | 91 + .../tools/power/cpupower/man/cpupower-idle-set.1 | 77 + kernel/tools/power/cpupower/man/cpupower-info.1 | 19 + kernel/tools/power/cpupower/man/cpupower-monitor.1 | 198 + kernel/tools/power/cpupower/man/cpupower-set.1 | 65 + kernel/tools/power/cpupower/man/cpupower.1 | 72 + kernel/tools/power/cpupower/po/cs.po | 944 +++ kernel/tools/power/cpupower/po/de.po | 961 +++ kernel/tools/power/cpupower/po/fr.po | 947 +++ kernel/tools/power/cpupower/po/it.po | 961 +++ kernel/tools/power/cpupower/po/pt.po | 957 +++ kernel/tools/power/cpupower/utils/builtin.h | 12 + kernel/tools/power/cpupower/utils/cpufreq-info.c | 698 +++ kernel/tools/power/cpupower/utils/cpufreq-set.c | 330 + kernel/tools/power/cpupower/utils/cpuidle-info.c | 208 + kernel/tools/power/cpupower/utils/cpuidle-set.c | 181 + kernel/tools/power/cpupower/utils/cpupower-info.c | 103 + kernel/tools/power/cpupower/utils/cpupower-set.c | 95 + kernel/tools/power/cpupower/utils/cpupower.c | 229 + kernel/tools/power/cpupower/utils/helpers/amd.c | 135 + .../tools/power/cpupower/utils/helpers/bitmask.c | 292 + .../tools/power/cpupower/utils/helpers/bitmask.h | 33 + kernel/tools/power/cpupower/utils/helpers/cpuid.c | 178 + .../tools/power/cpupower/utils/helpers/helpers.h | 195 + kernel/tools/power/cpupower/utils/helpers/misc.c | 27 + kernel/tools/power/cpupower/utils/helpers/msr.c | 115 + kernel/tools/power/cpupower/utils/helpers/pci.c | 62 + kernel/tools/power/cpupower/utils/helpers/sysfs.c | 472 ++ kernel/tools/power/cpupower/utils/helpers/sysfs.h | 38 + .../tools/power/cpupower/utils/helpers/topology.c | 116 + .../cpupower/utils/idle_monitor/amd_fam14h_idle.c | 335 + .../cpupower/utils/idle_monitor/cpuidle_sysfs.c | 196 + .../cpupower/utils/idle_monitor/cpupower-monitor.c | 455 ++ .../cpupower/utils/idle_monitor/cpupower-monitor.h | 85 + .../cpupower/utils/idle_monitor/hsw_ext_idle.c | 196 + .../cpupower/utils/idle_monitor/idle_monitors.def | 8 + .../cpupower/utils/idle_monitor/idle_monitors.h | 18 + .../cpupower/utils/idle_monitor/mperf_monitor.c | 338 + .../power/cpupower/utils/idle_monitor/nhm_idle.c | 216 + .../power/cpupower/utils/idle_monitor/snb_idle.c | 200 + kernel/tools/power/cpupower/utils/version-gen.sh | 35 + kernel/tools/power/x86/turbostat/.gitignore | 1 + kernel/tools/power/x86/turbostat/Makefile | 26 + kernel/tools/power/x86/turbostat/turbostat.8 | 273 + kernel/tools/power/x86/turbostat/turbostat.c | 3118 ++++++++++ .../power/x86/x86_energy_perf_policy/Makefile | 10 + .../x86_energy_perf_policy.8 | 104 + .../x86_energy_perf_policy.c | 324 + kernel/tools/scripts/Makefile.include | 84 + kernel/tools/testing/fault-injection/failcmd.sh | 219 + kernel/tools/testing/ktest/compare-ktest-sample.pl | 32 + kernel/tools/testing/ktest/examples/README | 32 + .../tools/testing/ktest/examples/crosstests.conf | 254 + .../testing/ktest/examples/include/bisect.conf | 90 + .../testing/ktest/examples/include/defaults.conf | 157 + .../testing/ktest/examples/include/min-config.conf | 60 + .../testing/ktest/examples/include/patchcheck.conf | 111 + .../testing/ktest/examples/include/tests.conf | 74 + kernel/tools/testing/ktest/examples/kvm.conf | 92 + kernel/tools/testing/ktest/examples/snowball.conf | 53 + kernel/tools/testing/ktest/examples/test.conf | 62 + kernel/tools/testing/ktest/ktest.pl | 4387 +++++++++++++ kernel/tools/testing/ktest/sample.conf | 1288 ++++ kernel/tools/testing/selftests/Makefile | 95 + .../tools/testing/selftests/breakpoints/Makefile | 24 + .../selftests/breakpoints/breakpoint_test.c | 396 ++ .../tools/testing/selftests/cpu-hotplug/Makefile | 10 + .../selftests/cpu-hotplug/cpu-on-off-test.sh | 269 + kernel/tools/testing/selftests/efivarfs/Makefile | 13 + .../tools/testing/selftests/efivarfs/create-read.c | 38 + .../tools/testing/selftests/efivarfs/efivarfs.sh | 198 + .../tools/testing/selftests/efivarfs/open-unlink.c | 63 + kernel/tools/testing/selftests/exec/.gitignore | 9 + kernel/tools/testing/selftests/exec/Makefile | 28 + kernel/tools/testing/selftests/exec/execveat.c | 412 ++ kernel/tools/testing/selftests/firmware/Makefile | 11 + .../testing/selftests/firmware/fw_filesystem.sh | 62 + .../testing/selftests/firmware/fw_userhelper.sh | 89 + kernel/tools/testing/selftests/ftrace/Makefile | 8 + kernel/tools/testing/selftests/ftrace/README | 82 + kernel/tools/testing/selftests/ftrace/ftracetest | 270 + .../tools/testing/selftests/ftrace/samples/fail.tc | 4 + .../tools/testing/selftests/ftrace/samples/pass.tc | 3 + .../testing/selftests/ftrace/samples/unresolved.tc | 4 + .../selftests/ftrace/samples/unsupported.tc | 3 + .../testing/selftests/ftrace/samples/untested.tc | 3 + .../testing/selftests/ftrace/samples/xfail.tc | 3 + .../selftests/ftrace/test.d/00basic/basic1.tc | 3 + .../selftests/ftrace/test.d/00basic/basic2.tc | 7 + .../selftests/ftrace/test.d/00basic/basic3.tc | 8 + .../selftests/ftrace/test.d/00basic/basic4.tc | 5 + .../selftests/ftrace/test.d/event/event-enable.tc | 60 + .../ftrace/test.d/event/subsystem-enable.tc | 60 + .../ftrace/test.d/event/toplevel-enable.tc | 60 + .../ftrace/test.d/ftrace/fgraph-filter-stack.tc | 91 + .../ftrace/test.d/ftrace/fgraph-filter.tc | 52 + .../ftrace/test.d/ftrace/func_profiler.tc | 80 + .../testing/selftests/ftrace/test.d/functions | 16 + .../ftrace/test.d/kprobe/add_and_remove.tc | 12 + .../selftests/ftrace/test.d/kprobe/busy_check.tc | 14 + .../selftests/ftrace/test.d/kprobe/kprobe_args.tc | 17 + .../ftrace/test.d/kprobe/kprobe_ftrace.tc | 55 + .../ftrace/test.d/kprobe/kretprobe_args.tc | 16 + .../tools/testing/selftests/ftrace/test.d/template | 9 + .../tools/testing/selftests/gen_kselftest_tar.sh | 55 + kernel/tools/testing/selftests/ipc/Makefile | 22 + kernel/tools/testing/selftests/ipc/msgque.c | 254 + kernel/tools/testing/selftests/kcmp/.gitignore | 2 + kernel/tools/testing/selftests/kcmp/Makefile | 10 + kernel/tools/testing/selftests/kcmp/kcmp_test.c | 109 + kernel/tools/testing/selftests/kselftest.h | 62 + .../tools/testing/selftests/kselftest_install.sh | 37 + kernel/tools/testing/selftests/lib.mk | 35 + kernel/tools/testing/selftests/memfd/.gitignore | 4 + kernel/tools/testing/selftests/memfd/Makefile | 22 + kernel/tools/testing/selftests/memfd/fuse_mnt.c | 110 + kernel/tools/testing/selftests/memfd/fuse_test.c | 311 + kernel/tools/testing/selftests/memfd/memfd_test.c | 911 +++ .../tools/testing/selftests/memfd/run_fuse_test.sh | 14 + .../testing/selftests/memory-hotplug/Makefile | 12 + .../selftests/memory-hotplug/mem-on-off-test.sh | 238 + kernel/tools/testing/selftests/mount/.gitignore | 1 + kernel/tools/testing/selftests/mount/Makefile | 16 + .../selftests/mount/unprivileged-remount-test.c | 370 ++ kernel/tools/testing/selftests/mqueue/.gitignore | 2 + kernel/tools/testing/selftests/mqueue/Makefile | 22 + .../tools/testing/selftests/mqueue/mq_open_tests.c | 500 ++ .../tools/testing/selftests/mqueue/mq_perf_tests.c | 742 +++ kernel/tools/testing/selftests/net/.gitignore | 3 + kernel/tools/testing/selftests/net/Makefile | 19 + kernel/tools/testing/selftests/net/psock_fanout.c | 312 + kernel/tools/testing/selftests/net/psock_lib.h | 127 + kernel/tools/testing/selftests/net/psock_tpacket.c | 805 +++ .../tools/testing/selftests/net/run_afpackettests | 26 + .../tools/testing/selftests/net/run_netsocktests | 12 + kernel/tools/testing/selftests/net/socket.c | 92 + kernel/tools/testing/selftests/net/test_bpf.sh | 10 + kernel/tools/testing/selftests/powerpc/Makefile | 53 + .../testing/selftests/powerpc/copyloops/.gitignore | 4 + .../testing/selftests/powerpc/copyloops/Makefile | 25 + .../selftests/powerpc/copyloops/asm/ppc_asm.h | 56 + .../selftests/powerpc/copyloops/asm/processor.h | 0 .../selftests/powerpc/copyloops/copyuser_64.S | 1 + .../selftests/powerpc/copyloops/copyuser_power7.S | 1 + .../selftests/powerpc/copyloops/memcpy_64.S | 1 + .../selftests/powerpc/copyloops/memcpy_power7.S | 1 + .../testing/selftests/powerpc/copyloops/validate.c | 99 + kernel/tools/testing/selftests/powerpc/harness.c | 161 + .../tools/testing/selftests/powerpc/mm/.gitignore | 3 + kernel/tools/testing/selftests/powerpc/mm/Makefile | 16 + .../selftests/powerpc/mm/hugetlb_vs_thp_test.c | 76 + .../testing/selftests/powerpc/mm/subpage_prot.c | 220 + .../tools/testing/selftests/powerpc/pmu/.gitignore | 3 + .../tools/testing/selftests/powerpc/pmu/Makefile | 42 + .../selftests/powerpc/pmu/count_instructions.c | 147 + .../testing/selftests/powerpc/pmu/ebb/.gitignore | 22 + .../testing/selftests/powerpc/pmu/ebb/Makefile | 30 + .../powerpc/pmu/ebb/back_to_back_ebbs_test.c | 106 + .../testing/selftests/powerpc/pmu/ebb/busy_loop.S | 271 + .../powerpc/pmu/ebb/close_clears_pmcc_test.c | 59 + .../powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c | 93 + .../powerpc/pmu/ebb/cpu_event_vs_ebb_test.c | 89 + .../selftests/powerpc/pmu/ebb/cycles_test.c | 58 + .../powerpc/pmu/ebb/cycles_with_freeze_test.c | 117 + .../powerpc/pmu/ebb/cycles_with_mmcr2_test.c | 91 + .../tools/testing/selftests/powerpc/pmu/ebb/ebb.c | 478 ++ .../tools/testing/selftests/powerpc/pmu/ebb/ebb.h | 77 + .../selftests/powerpc/pmu/ebb/ebb_handler.S | 365 ++ .../selftests/powerpc/pmu/ebb/ebb_on_child_test.c | 86 + .../powerpc/pmu/ebb/ebb_on_willing_child_test.c | 92 + .../powerpc/pmu/ebb/ebb_vs_cpu_event_test.c | 86 + .../powerpc/pmu/ebb/event_attributes_test.c | 131 + .../powerpc/pmu/ebb/fixed_instruction_loop.S | 43 + .../selftests/powerpc/pmu/ebb/fork_cleanup_test.c | 79 + .../powerpc/pmu/ebb/instruction_count_test.c | 164 + .../powerpc/pmu/ebb/lost_exception_test.c | 100 + .../selftests/powerpc/pmu/ebb/multi_counter_test.c | 91 + .../powerpc/pmu/ebb/multi_ebb_procs_test.c | 109 + .../selftests/powerpc/pmu/ebb/no_handler_test.c | 61 + .../selftests/powerpc/pmu/ebb/pmae_handling_test.c | 106 + .../powerpc/pmu/ebb/pmc56_overflow_test.c | 93 + .../tools/testing/selftests/powerpc/pmu/ebb/reg.h | 49 + .../selftests/powerpc/pmu/ebb/reg_access_test.c | 39 + .../pmu/ebb/task_event_pinned_vs_ebb_test.c | 91 + .../powerpc/pmu/ebb/task_event_vs_ebb_test.c | 83 + .../testing/selftests/powerpc/pmu/ebb/trace.c | 300 + .../testing/selftests/powerpc/pmu/ebb/trace.h | 41 + kernel/tools/testing/selftests/powerpc/pmu/event.c | 131 + kernel/tools/testing/selftests/powerpc/pmu/event.h | 43 + .../testing/selftests/powerpc/pmu/l3_bank_test.c | 48 + kernel/tools/testing/selftests/powerpc/pmu/lib.c | 253 + kernel/tools/testing/selftests/powerpc/pmu/lib.h | 41 + kernel/tools/testing/selftests/powerpc/pmu/loop.S | 43 + .../selftests/powerpc/pmu/per_event_excludes.c | 114 + .../selftests/powerpc/primitives/.gitignore | 1 + .../testing/selftests/powerpc/primitives/Makefile | 12 + .../selftests/powerpc/primitives/asm/asm-compat.h | 1 + .../selftests/powerpc/primitives/asm/ppc-opcode.h | 0 .../powerpc/primitives/load_unaligned_zeropad.c | 147 + .../selftests/powerpc/primitives/word-at-a-time.h | 1 + .../selftests/powerpc/stringloops/.gitignore | 1 + .../testing/selftests/powerpc/stringloops/Makefile | 15 + .../selftests/powerpc/stringloops/asm/ppc_asm.h | 7 + .../testing/selftests/powerpc/stringloops/memcmp.c | 103 + .../selftests/powerpc/stringloops/memcmp_64.S | 1 + kernel/tools/testing/selftests/powerpc/subunit.h | 52 + .../selftests/powerpc/switch_endian/.gitignore | 2 + .../selftests/powerpc/switch_endian/Makefile | 24 + .../selftests/powerpc/switch_endian/check.S | 100 + .../selftests/powerpc/switch_endian/common.h | 6 + .../powerpc/switch_endian/switch_endian_test.S | 81 + .../tools/testing/selftests/powerpc/tm/.gitignore | 2 + kernel/tools/testing/selftests/powerpc/tm/Makefile | 13 + .../testing/selftests/powerpc/tm/tm-resched-dscr.c | 98 + .../testing/selftests/powerpc/tm/tm-syscall-asm.S | 27 + .../testing/selftests/powerpc/tm/tm-syscall.c | 121 + kernel/tools/testing/selftests/powerpc/utils.h | 50 + .../testing/selftests/powerpc/vphn/.gitignore | 1 + .../tools/testing/selftests/powerpc/vphn/Makefile | 15 + .../testing/selftests/powerpc/vphn/test-vphn.c | 410 ++ kernel/tools/testing/selftests/powerpc/vphn/vphn.c | 1 + kernel/tools/testing/selftests/powerpc/vphn/vphn.h | 1 + kernel/tools/testing/selftests/ptrace/Makefile | 11 + .../tools/testing/selftests/ptrace/peeksiginfo.c | 218 + .../tools/testing/selftests/rcutorture/.gitignore | 6 + .../selftests/rcutorture/bin/config2frag.sh | 25 + .../selftests/rcutorture/bin/configNR_CPUS.sh | 45 + .../selftests/rcutorture/bin/configcheck.sh | 54 + .../testing/selftests/rcutorture/bin/configinit.sh | 74 + .../testing/selftests/rcutorture/bin/cpus2use.sh | 41 + .../testing/selftests/rcutorture/bin/functions.sh | 243 + .../testing/selftests/rcutorture/bin/kvm-build.sh | 71 + .../selftests/rcutorture/bin/kvm-recheck-lock.sh | 51 + .../selftests/rcutorture/bin/kvm-recheck-rcu.sh | 69 + .../selftests/rcutorture/bin/kvm-recheck.sh | 69 + .../selftests/rcutorture/bin/kvm-test-1-run.sh | 229 + .../tools/testing/selftests/rcutorture/bin/kvm.sh | 406 ++ .../selftests/rcutorture/bin/parse-build.sh | 62 + .../selftests/rcutorture/bin/parse-console.sh | 44 + .../selftests/rcutorture/bin/parse-torture.sh | 105 + .../selftests/rcutorture/configs/lock/BUSTED | 6 + .../selftests/rcutorture/configs/lock/BUSTED.boot | 1 + .../selftests/rcutorture/configs/lock/CFLIST | 4 + .../selftests/rcutorture/configs/lock/CFcommon | 2 + .../selftests/rcutorture/configs/lock/LOCK01 | 6 + .../selftests/rcutorture/configs/lock/LOCK02 | 6 + .../selftests/rcutorture/configs/lock/LOCK02.boot | 1 + .../selftests/rcutorture/configs/lock/LOCK03 | 6 + .../selftests/rcutorture/configs/lock/LOCK03.boot | 1 + .../selftests/rcutorture/configs/lock/LOCK04 | 6 + .../selftests/rcutorture/configs/lock/LOCK04.boot | 1 + .../rcutorture/configs/lock/ver_functions.sh | 43 + .../selftests/rcutorture/configs/rcu/BUSTED | 7 + .../selftests/rcutorture/configs/rcu/BUSTED.boot | 1 + .../selftests/rcutorture/configs/rcu/CFLIST | 16 + .../selftests/rcutorture/configs/rcu/CFcommon | 3 + .../selftests/rcutorture/configs/rcu/SRCU-N | 7 + .../selftests/rcutorture/configs/rcu/SRCU-N.boot | 1 + .../selftests/rcutorture/configs/rcu/SRCU-P | 7 + .../selftests/rcutorture/configs/rcu/SRCU-P.boot | 1 + .../selftests/rcutorture/configs/rcu/TASKS01 | 9 + .../selftests/rcutorture/configs/rcu/TASKS01.boot | 1 + .../selftests/rcutorture/configs/rcu/TASKS02 | 5 + .../selftests/rcutorture/configs/rcu/TASKS02.boot | 1 + .../selftests/rcutorture/configs/rcu/TASKS03 | 13 + .../selftests/rcutorture/configs/rcu/TASKS03.boot | 1 + .../selftests/rcutorture/configs/rcu/TINY01 | 12 + .../selftests/rcutorture/configs/rcu/TINY02 | 14 + .../selftests/rcutorture/configs/rcu/TINY02.boot | 2 + .../selftests/rcutorture/configs/rcu/TREE01 | 18 + .../selftests/rcutorture/configs/rcu/TREE01.boot | 1 + .../selftests/rcutorture/configs/rcu/TREE02 | 23 + .../selftests/rcutorture/configs/rcu/TREE02-T | 23 + .../selftests/rcutorture/configs/rcu/TREE03 | 20 + .../selftests/rcutorture/configs/rcu/TREE04 | 22 + .../selftests/rcutorture/configs/rcu/TREE04.boot | 1 + .../selftests/rcutorture/configs/rcu/TREE05 | 22 + .../selftests/rcutorture/configs/rcu/TREE05.boot | 2 + .../selftests/rcutorture/configs/rcu/TREE06 | 23 + .../selftests/rcutorture/configs/rcu/TREE06.boot | 3 + .../selftests/rcutorture/configs/rcu/TREE07 | 22 + .../selftests/rcutorture/configs/rcu/TREE07.boot | 1 + .../selftests/rcutorture/configs/rcu/TREE08 | 25 + .../selftests/rcutorture/configs/rcu/TREE08-T | 23 + .../selftests/rcutorture/configs/rcu/TREE08.boot | 3 + .../selftests/rcutorture/configs/rcu/TREE09 | 18 + .../rcutorture/configs/rcu/ver_functions.sh | 57 + .../testing/selftests/rcutorture/doc/TINY_RCU.txt | 40 + .../selftests/rcutorture/doc/TREE_RCU-kconfig.txt | 93 + .../testing/selftests/rcutorture/doc/initrd.txt | 91 + .../selftests/rcutorture/doc/rcu-test-image.txt | 42 + kernel/tools/testing/selftests/size/.gitignore | 1 + kernel/tools/testing/selftests/size/Makefile | 11 + kernel/tools/testing/selftests/size/get_size.c | 100 + kernel/tools/testing/selftests/sysctl/Makefile | 13 + kernel/tools/testing/selftests/sysctl/common_tests | 109 + .../testing/selftests/sysctl/run_numerictests | 10 + .../tools/testing/selftests/sysctl/run_stringtests | 77 + kernel/tools/testing/selftests/timers/Makefile | 36 + .../testing/selftests/timers/alarmtimer-suspend.c | 185 + .../tools/testing/selftests/timers/change_skew.c | 107 + .../testing/selftests/timers/clocksource-switch.c | 179 + .../testing/selftests/timers/inconsistency-check.c | 204 + kernel/tools/testing/selftests/timers/leap-a-day.c | 319 + kernel/tools/testing/selftests/timers/leapcrash.c | 120 + kernel/tools/testing/selftests/timers/mqueue-lat.c | 124 + kernel/tools/testing/selftests/timers/nanosleep.c | 174 + kernel/tools/testing/selftests/timers/nsleep-lat.c | 190 + .../tools/testing/selftests/timers/posix_timers.c | 222 + kernel/tools/testing/selftests/timers/raw_skew.c | 154 + kernel/tools/testing/selftests/timers/rtctest.c | 271 + kernel/tools/testing/selftests/timers/set-2038.c | 144 + kernel/tools/testing/selftests/timers/set-tai.c | 79 + .../tools/testing/selftests/timers/set-timer-lat.c | 216 + .../testing/selftests/timers/skew_consistency.c | 89 + kernel/tools/testing/selftests/timers/threadtest.c | 204 + .../testing/selftests/timers/valid-adjtimex.c | 202 + kernel/tools/testing/selftests/user/Makefile | 8 + .../tools/testing/selftests/user/test_user_copy.sh | 10 + kernel/tools/testing/selftests/vm/.gitignore | 4 + kernel/tools/testing/selftests/vm/Makefile | 17 + kernel/tools/testing/selftests/vm/hugepage-mmap.c | 92 + kernel/tools/testing/selftests/vm/hugepage-shm.c | 100 + kernel/tools/testing/selftests/vm/hugetlbfstest.c | 86 + kernel/tools/testing/selftests/vm/map_hugetlb.c | 83 + kernel/tools/testing/selftests/vm/run_vmtests | 93 + kernel/tools/testing/selftests/vm/thuge-gen.c | 254 + .../tools/testing/selftests/vm/transhuge-stress.c | 144 + kernel/tools/testing/selftests/x86/.gitignore | 2 + kernel/tools/testing/selftests/x86/Makefile | 59 + kernel/tools/testing/selftests/x86/check_cc.sh | 16 + .../tools/testing/selftests/x86/entry_from_vm86.c | 114 + kernel/tools/testing/selftests/x86/sigreturn.c | 684 ++ .../testing/selftests/x86/single_step_syscall.c | 181 + .../testing/selftests/x86/trivial_32bit_program.c | 18 + .../testing/selftests/x86/trivial_64bit_program.c | 18 + kernel/tools/thermal/tmon/.gitignore | 1 + kernel/tools/thermal/tmon/Makefile | 48 + kernel/tools/thermal/tmon/README | 50 + kernel/tools/thermal/tmon/pid.c | 131 + kernel/tools/thermal/tmon/sysfs.c | 596 ++ kernel/tools/thermal/tmon/tmon.8 | 144 + kernel/tools/thermal/tmon/tmon.c | 386 ++ kernel/tools/thermal/tmon/tmon.h | 204 + kernel/tools/thermal/tmon/tui.c | 665 ++ kernel/tools/time/udelay_test.sh | 66 + kernel/tools/usb/Makefile | 14 + .../multibuff/device_app/aio_multibuff.c | 380 ++ .../ffs-aio-example/multibuff/host_app/Makefile | 13 + .../usb/ffs-aio-example/multibuff/host_app/test.c | 175 + .../ffs-aio-example/simple/device_app/aio_simple.c | 368 ++ .../usb/ffs-aio-example/simple/host_app/Makefile | 13 + .../usb/ffs-aio-example/simple/host_app/test.c | 178 + kernel/tools/usb/ffs-test.c | 633 ++ kernel/tools/usb/hcd-tests.sh | 275 + kernel/tools/usb/testusb.c | 537 ++ kernel/tools/usb/usbip/.gitignore | 28 + kernel/tools/usb/usbip/AUTHORS | 3 + kernel/tools/usb/usbip/COPYING | 340 + kernel/tools/usb/usbip/INSTALL | 237 + kernel/tools/usb/usbip/Makefile.am | 6 + kernel/tools/usb/usbip/README | 202 + kernel/tools/usb/usbip/autogen.sh | 9 + kernel/tools/usb/usbip/cleanup.sh | 12 + kernel/tools/usb/usbip/configure.ac | 111 + kernel/tools/usb/usbip/doc/usbip.8 | 95 + kernel/tools/usb/usbip/doc/usbipd.8 | 91 + kernel/tools/usb/usbip/libsrc/Makefile.am | 8 + kernel/tools/usb/usbip/libsrc/list.h | 136 + kernel/tools/usb/usbip/libsrc/names.c | 504 ++ kernel/tools/usb/usbip/libsrc/names.h | 41 + kernel/tools/usb/usbip/libsrc/sysfs_utils.c | 31 + kernel/tools/usb/usbip/libsrc/sysfs_utils.h | 8 + kernel/tools/usb/usbip/libsrc/usbip_common.c | 285 + kernel/tools/usb/usbip/libsrc/usbip_common.h | 137 + kernel/tools/usb/usbip/libsrc/usbip_host_driver.c | 280 + kernel/tools/usb/usbip/libsrc/usbip_host_driver.h | 49 + kernel/tools/usb/usbip/libsrc/vhci_driver.c | 411 ++ kernel/tools/usb/usbip/libsrc/vhci_driver.h | 59 + kernel/tools/usb/usbip/src/Makefile.am | 11 + kernel/tools/usb/usbip/src/usbip.c | 201 + kernel/tools/usb/usbip/src/usbip.h | 40 + kernel/tools/usb/usbip/src/usbip_attach.c | 241 + kernel/tools/usb/usbip/src/usbip_bind.c | 214 + kernel/tools/usb/usbip/src/usbip_detach.c | 110 + kernel/tools/usb/usbip/src/usbip_list.c | 283 + kernel/tools/usb/usbip/src/usbip_network.c | 303 + kernel/tools/usb/usbip/src/usbip_network.h | 185 + kernel/tools/usb/usbip/src/usbip_port.c | 57 + kernel/tools/usb/usbip/src/usbip_unbind.c | 141 + kernel/tools/usb/usbip/src/usbipd.c | 677 ++ kernel/tools/usb/usbip/src/utils.c | 52 + kernel/tools/usb/usbip/src/utils.h | 25 + kernel/tools/virtio/.gitignore | 3 + kernel/tools/virtio/Makefile | 14 + kernel/tools/virtio/asm/barrier.h | 14 + kernel/tools/virtio/linux/bug.h | 10 + kernel/tools/virtio/linux/device.h | 2 + kernel/tools/virtio/linux/err.h | 26 + kernel/tools/virtio/linux/hrtimer.h | 0 kernel/tools/virtio/linux/irqreturn.h | 1 + kernel/tools/virtio/linux/kernel.h | 105 + kernel/tools/virtio/linux/kmemleak.h | 3 + kernel/tools/virtio/linux/module.h | 6 + kernel/tools/virtio/linux/printk.h | 4 + kernel/tools/virtio/linux/ratelimit.h | 4 + kernel/tools/virtio/linux/scatterlist.h | 189 + kernel/tools/virtio/linux/slab.h | 2 + kernel/tools/virtio/linux/uaccess.h | 50 + kernel/tools/virtio/linux/uio.h | 3 + kernel/tools/virtio/linux/virtio.h | 68 + kernel/tools/virtio/linux/virtio_byteorder.h | 8 + kernel/tools/virtio/linux/virtio_config.h | 72 + kernel/tools/virtio/linux/virtio_ring.h | 1 + kernel/tools/virtio/linux/vringh.h | 1 + kernel/tools/virtio/uapi/linux/uio.h | 1 + kernel/tools/virtio/uapi/linux/virtio_config.h | 1 + kernel/tools/virtio/uapi/linux/virtio_ring.h | 4 + kernel/tools/virtio/uapi/linux/virtio_types.h | 1 + kernel/tools/virtio/vhost_test/Makefile | 2 + kernel/tools/virtio/vhost_test/vhost_test.c | 1 + kernel/tools/virtio/virtio-trace/Makefile | 13 + kernel/tools/virtio/virtio-trace/README | 118 + kernel/tools/virtio/virtio-trace/trace-agent-ctl.c | 137 + kernel/tools/virtio/virtio-trace/trace-agent-rw.c | 192 + kernel/tools/virtio/virtio-trace/trace-agent.c | 270 + kernel/tools/virtio/virtio-trace/trace-agent.h | 75 + kernel/tools/virtio/virtio_test.c | 302 + kernel/tools/virtio/vringh_test.c | 749 +++ kernel/tools/vm/.gitignore | 2 + kernel/tools/vm/Makefile | 22 + kernel/tools/vm/page-types.c | 1178 ++++ kernel/tools/vm/page_owner_sort.c | 144 + kernel/tools/vm/slabinfo.c | 1393 +++++ 1184 files changed, 215164 insertions(+) create mode 100644 kernel/tools/Makefile create mode 100644 kernel/tools/build/Build.include create mode 100644 kernel/tools/build/Documentation/Build.txt create mode 100644 kernel/tools/build/Makefile.build create mode 100644 kernel/tools/build/Makefile.feature create mode 100644 kernel/tools/build/feature/.gitignore create mode 100644 kernel/tools/build/feature/Makefile create mode 100644 kernel/tools/build/feature/test-all.c create mode 100644 kernel/tools/build/feature/test-backtrace.c create mode 100644 kernel/tools/build/feature/test-bionic.c create mode 100644 kernel/tools/build/feature/test-compile.c create mode 100644 kernel/tools/build/feature/test-cplus-demangle.c create mode 100644 kernel/tools/build/feature/test-dwarf.c create mode 100644 kernel/tools/build/feature/test-fortify-source.c create mode 100644 kernel/tools/build/feature/test-glibc.c create mode 100644 kernel/tools/build/feature/test-gtk2-infobar.c create mode 100644 kernel/tools/build/feature/test-gtk2.c create mode 100644 kernel/tools/build/feature/test-hello.c create mode 100644 kernel/tools/build/feature/test-libaudit.c create mode 100644 kernel/tools/build/feature/test-libbabeltrace.c create mode 100644 kernel/tools/build/feature/test-libbfd.c create mode 100644 kernel/tools/build/feature/test-libdw-dwarf-unwind.c create mode 100644 kernel/tools/build/feature/test-libelf-getphdrnum.c create mode 100644 kernel/tools/build/feature/test-libelf-mmap.c create mode 100644 kernel/tools/build/feature/test-libelf.c create mode 100644 kernel/tools/build/feature/test-libnuma.c create mode 100644 kernel/tools/build/feature/test-libperl.c create mode 100644 kernel/tools/build/feature/test-libpython-version.c create mode 100644 kernel/tools/build/feature/test-libpython.c create mode 100644 kernel/tools/build/feature/test-libslang.c create mode 100644 kernel/tools/build/feature/test-libunwind-debug-frame.c create mode 100644 kernel/tools/build/feature/test-libunwind.c create mode 100644 kernel/tools/build/feature/test-lzma.c create mode 100644 kernel/tools/build/feature/test-pthread-attr-setaffinity-np.c create mode 100644 kernel/tools/build/feature/test-stackprotector-all.c create mode 100644 kernel/tools/build/feature/test-sync-compare-and-swap.c create mode 100644 kernel/tools/build/feature/test-timerfd.c create mode 100644 kernel/tools/build/feature/test-zlib.c create mode 100644 kernel/tools/build/tests/ex/Build create mode 100644 kernel/tools/build/tests/ex/Makefile create mode 100644 kernel/tools/build/tests/ex/a.c create mode 100644 kernel/tools/build/tests/ex/arch/Build create mode 100644 kernel/tools/build/tests/ex/arch/e.c create mode 100644 kernel/tools/build/tests/ex/arch/f.c create mode 100644 kernel/tools/build/tests/ex/b.c create mode 100644 kernel/tools/build/tests/ex/c.c create mode 100644 kernel/tools/build/tests/ex/d.c create mode 100644 kernel/tools/build/tests/ex/empty/Build create mode 100644 kernel/tools/build/tests/ex/ex.c create mode 100755 kernel/tools/build/tests/run.sh create mode 100644 kernel/tools/cgroup/.gitignore create mode 100644 kernel/tools/cgroup/Makefile create mode 100644 kernel/tools/cgroup/cgroup_event_listener.c create mode 100644 kernel/tools/firewire/Makefile create mode 100644 kernel/tools/firewire/decode-fcp.c create mode 100644 kernel/tools/firewire/list.h create mode 100644 kernel/tools/firewire/nosy-dump.c create mode 100644 kernel/tools/firewire/nosy-dump.h create mode 100644 kernel/tools/hv/Makefile create mode 100644 kernel/tools/hv/hv_fcopy_daemon.c create mode 100755 kernel/tools/hv/hv_get_dhcp_info.sh create mode 100755 kernel/tools/hv/hv_get_dns_info.sh create mode 100644 kernel/tools/hv/hv_kvp_daemon.c create mode 100755 kernel/tools/hv/hv_set_ifconfig.sh create mode 100644 kernel/tools/hv/hv_vss_daemon.c create mode 100644 kernel/tools/iio/Makefile create mode 100644 kernel/tools/iio/generic_buffer.c create mode 100644 kernel/tools/iio/iio_event_monitor.c create mode 100644 kernel/tools/iio/iio_utils.c create mode 100644 kernel/tools/iio/iio_utils.h create mode 100644 kernel/tools/iio/lsiio.c create mode 100644 kernel/tools/include/asm-generic/bitops.h create mode 100644 kernel/tools/include/asm-generic/bitops/__ffs.h create mode 100644 kernel/tools/include/asm-generic/bitops/__fls.h create mode 100644 kernel/tools/include/asm-generic/bitops/arch_hweight.h create mode 100644 kernel/tools/include/asm-generic/bitops/atomic.h create mode 100644 kernel/tools/include/asm-generic/bitops/const_hweight.h create mode 100644 kernel/tools/include/asm-generic/bitops/find.h create mode 100644 kernel/tools/include/asm-generic/bitops/fls.h create mode 100644 kernel/tools/include/asm-generic/bitops/fls64.h create mode 100644 kernel/tools/include/asm-generic/bitops/hweight.h create mode 100644 kernel/tools/include/asm/bug.h create mode 100644 kernel/tools/include/linux/bitops.h create mode 100644 kernel/tools/include/linux/compiler.h create mode 100644 kernel/tools/include/linux/export.h create mode 100644 kernel/tools/include/linux/hash.h create mode 100644 kernel/tools/include/linux/log2.h create mode 100644 kernel/tools/include/linux/types.h create mode 100644 kernel/tools/include/tools/be_byteshift.h create mode 100644 kernel/tools/include/tools/endian.h create mode 100644 kernel/tools/include/tools/le_byteshift.h create mode 100644 kernel/tools/lguest/.gitignore create mode 100644 kernel/tools/lguest/Makefile create mode 100644 kernel/tools/lguest/extract create mode 100644 kernel/tools/lguest/lguest.c create mode 100644 kernel/tools/lguest/lguest.txt create mode 100644 kernel/tools/lib/api/Build create mode 100644 kernel/tools/lib/api/Makefile create mode 100644 kernel/tools/lib/api/fd/Build create mode 100644 kernel/tools/lib/api/fd/array.c create mode 100644 kernel/tools/lib/api/fd/array.h create mode 100644 kernel/tools/lib/api/fs/Build create mode 100644 kernel/tools/lib/api/fs/debugfs.c create mode 100644 kernel/tools/lib/api/fs/debugfs.h create mode 100644 kernel/tools/lib/api/fs/findfs.c create mode 100644 kernel/tools/lib/api/fs/findfs.h create mode 100644 kernel/tools/lib/api/fs/fs.c create mode 100644 kernel/tools/lib/api/fs/fs.h create mode 100644 kernel/tools/lib/api/fs/tracefs.c create mode 100644 kernel/tools/lib/api/fs/tracefs.h create mode 100644 kernel/tools/lib/lockdep/.gitignore create mode 100644 kernel/tools/lib/lockdep/Build create mode 100644 kernel/tools/lib/lockdep/Makefile create mode 100644 kernel/tools/lib/lockdep/common.c create mode 100644 kernel/tools/lib/lockdep/include/liblockdep/common.h create mode 100644 kernel/tools/lib/lockdep/include/liblockdep/mutex.h create mode 100644 kernel/tools/lib/lockdep/include/liblockdep/rwlock.h create mode 100755 kernel/tools/lib/lockdep/lockdep create mode 100644 kernel/tools/lib/lockdep/lockdep.c create mode 100644 kernel/tools/lib/lockdep/lockdep_internals.h create mode 100644 kernel/tools/lib/lockdep/lockdep_states.h create mode 100644 kernel/tools/lib/lockdep/preload.c create mode 100644 kernel/tools/lib/lockdep/rbtree.c create mode 100755 kernel/tools/lib/lockdep/run_tests.sh create mode 100644 kernel/tools/lib/lockdep/tests/AA.c create mode 100644 kernel/tools/lib/lockdep/tests/ABBA.c create mode 100644 kernel/tools/lib/lockdep/tests/ABBCCA.c create mode 100644 kernel/tools/lib/lockdep/tests/ABBCCDDA.c create mode 100644 kernel/tools/lib/lockdep/tests/ABCABC.c create mode 100644 kernel/tools/lib/lockdep/tests/ABCDBCDA.c create mode 100644 kernel/tools/lib/lockdep/tests/ABCDBDDA.c create mode 100644 kernel/tools/lib/lockdep/tests/WW.c create mode 100644 kernel/tools/lib/lockdep/tests/common.h create mode 100644 kernel/tools/lib/lockdep/tests/unlock_balance.c create mode 100644 kernel/tools/lib/lockdep/uinclude/asm/hash.h create mode 100644 kernel/tools/lib/lockdep/uinclude/asm/hweight.h create mode 100644 kernel/tools/lib/lockdep/uinclude/asm/sections.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/bitops.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/compiler.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/debug_locks.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/delay.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/ftrace.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/gfp.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/hardirq.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/hash.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/interrupt.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/irqflags.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/kallsyms.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/kern_levels.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/kernel.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/kmemcheck.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/linkage.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/list.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/lockdep.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/module.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/mutex.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/poison.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/prefetch.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/proc_fs.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/rbtree.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/rbtree_augmented.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/rcu.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/seq_file.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/spinlock.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/stacktrace.h create mode 100644 kernel/tools/lib/lockdep/uinclude/linux/stringify.h create mode 100644 kernel/tools/lib/lockdep/uinclude/trace/events/lock.h create mode 100644 kernel/tools/lib/symbol/kallsyms.c create mode 100644 kernel/tools/lib/symbol/kallsyms.h create mode 100644 kernel/tools/lib/traceevent/.gitignore create mode 100644 kernel/tools/lib/traceevent/Build create mode 100644 kernel/tools/lib/traceevent/Makefile create mode 100644 kernel/tools/lib/traceevent/event-parse.c create mode 100644 kernel/tools/lib/traceevent/event-parse.h create mode 100644 kernel/tools/lib/traceevent/event-plugin.c create mode 100644 kernel/tools/lib/traceevent/event-utils.h create mode 100644 kernel/tools/lib/traceevent/kbuffer-parse.c create mode 100644 kernel/tools/lib/traceevent/kbuffer.h create mode 100644 kernel/tools/lib/traceevent/parse-filter.c create mode 100644 kernel/tools/lib/traceevent/parse-utils.c create mode 100644 kernel/tools/lib/traceevent/plugin_cfg80211.c create mode 100644 kernel/tools/lib/traceevent/plugin_function.c create mode 100644 kernel/tools/lib/traceevent/plugin_hrtimer.c create mode 100644 kernel/tools/lib/traceevent/plugin_jbd2.c create mode 100644 kernel/tools/lib/traceevent/plugin_kmem.c create mode 100644 kernel/tools/lib/traceevent/plugin_kvm.c create mode 100644 kernel/tools/lib/traceevent/plugin_mac80211.c create mode 100644 kernel/tools/lib/traceevent/plugin_sched_switch.c create mode 100644 kernel/tools/lib/traceevent/plugin_scsi.c create mode 100644 kernel/tools/lib/traceevent/plugin_xen.c create mode 100644 kernel/tools/lib/traceevent/trace-seq.c create mode 100644 kernel/tools/lib/util/find_next_bit.c create mode 100644 kernel/tools/net/Makefile create mode 100644 kernel/tools/net/bpf_asm.c create mode 100644 kernel/tools/net/bpf_dbg.c create mode 100644 kernel/tools/net/bpf_exp.l create mode 100644 kernel/tools/net/bpf_exp.y create mode 100644 kernel/tools/net/bpf_jit_disasm.c create mode 100755 kernel/tools/nfsd/inject_fault.sh create mode 100644 kernel/tools/perf/.gitignore create mode 100644 kernel/tools/perf/Build create mode 100644 kernel/tools/perf/CREDITS create mode 100644 kernel/tools/perf/Documentation/Build.txt create mode 100644 kernel/tools/perf/Documentation/Makefile create mode 100644 kernel/tools/perf/Documentation/android.txt create mode 100644 kernel/tools/perf/Documentation/asciidoc.conf create mode 100644 kernel/tools/perf/Documentation/examples.txt create mode 100644 kernel/tools/perf/Documentation/jit-interface.txt create mode 100644 kernel/tools/perf/Documentation/manpage-1.72.xsl create mode 100644 kernel/tools/perf/Documentation/manpage-base.xsl create mode 100644 kernel/tools/perf/Documentation/manpage-bold-literal.xsl create mode 100644 kernel/tools/perf/Documentation/manpage-normal.xsl create mode 100644 kernel/tools/perf/Documentation/manpage-suppress-sp.xsl create mode 100644 kernel/tools/perf/Documentation/perf-annotate.txt create mode 100644 kernel/tools/perf/Documentation/perf-archive.txt create mode 100644 kernel/tools/perf/Documentation/perf-bench.txt create mode 100644 kernel/tools/perf/Documentation/perf-buildid-cache.txt create mode 100644 kernel/tools/perf/Documentation/perf-buildid-list.txt create mode 100644 kernel/tools/perf/Documentation/perf-data.txt create mode 100644 kernel/tools/perf/Documentation/perf-diff.txt create mode 100644 kernel/tools/perf/Documentation/perf-evlist.txt create mode 100644 kernel/tools/perf/Documentation/perf-help.txt create mode 100644 kernel/tools/perf/Documentation/perf-inject.txt create mode 100644 kernel/tools/perf/Documentation/perf-kmem.txt create mode 100644 kernel/tools/perf/Documentation/perf-kvm.txt create mode 100644 kernel/tools/perf/Documentation/perf-list.txt create mode 100644 kernel/tools/perf/Documentation/perf-lock.txt create mode 100644 kernel/tools/perf/Documentation/perf-mem.txt create mode 100644 kernel/tools/perf/Documentation/perf-probe.txt create mode 100644 kernel/tools/perf/Documentation/perf-record.txt create mode 100644 kernel/tools/perf/Documentation/perf-report.txt create mode 100644 kernel/tools/perf/Documentation/perf-sched.txt create mode 100644 kernel/tools/perf/Documentation/perf-script-perl.txt create mode 100644 kernel/tools/perf/Documentation/perf-script-python.txt create mode 100644 kernel/tools/perf/Documentation/perf-script.txt create mode 100644 kernel/tools/perf/Documentation/perf-stat.txt create mode 100644 kernel/tools/perf/Documentation/perf-test.txt create mode 100644 kernel/tools/perf/Documentation/perf-timechart.txt create mode 100644 kernel/tools/perf/Documentation/perf-top.txt create mode 100644 kernel/tools/perf/Documentation/perf-trace.txt create mode 100644 kernel/tools/perf/Documentation/perf.txt create mode 100644 kernel/tools/perf/Documentation/perfconfig.example create mode 100644 kernel/tools/perf/MANIFEST create mode 100644 kernel/tools/perf/Makefile create mode 100644 kernel/tools/perf/Makefile.perf create mode 100644 kernel/tools/perf/arch/Build create mode 100644 kernel/tools/perf/arch/arm/Build create mode 100644 kernel/tools/perf/arch/arm/Makefile create mode 100644 kernel/tools/perf/arch/arm/include/perf_regs.h create mode 100644 kernel/tools/perf/arch/arm/tests/Build create mode 100644 kernel/tools/perf/arch/arm/tests/dwarf-unwind.c create mode 100644 kernel/tools/perf/arch/arm/tests/regs_load.S create mode 100644 kernel/tools/perf/arch/arm/util/Build create mode 100644 kernel/tools/perf/arch/arm/util/dwarf-regs.c create mode 100644 kernel/tools/perf/arch/arm/util/unwind-libdw.c create mode 100644 kernel/tools/perf/arch/arm/util/unwind-libunwind.c create mode 100644 kernel/tools/perf/arch/arm64/Build create mode 100644 kernel/tools/perf/arch/arm64/Makefile create mode 100644 kernel/tools/perf/arch/arm64/include/perf_regs.h create mode 100644 kernel/tools/perf/arch/arm64/util/Build create mode 100644 kernel/tools/perf/arch/arm64/util/dwarf-regs.c create mode 100644 kernel/tools/perf/arch/arm64/util/unwind-libunwind.c create mode 100644 kernel/tools/perf/arch/common.c create mode 100644 kernel/tools/perf/arch/common.h create mode 100644 kernel/tools/perf/arch/powerpc/Build create mode 100644 kernel/tools/perf/arch/powerpc/Makefile create mode 100644 kernel/tools/perf/arch/powerpc/util/Build create mode 100644 kernel/tools/perf/arch/powerpc/util/dwarf-regs.c create mode 100644 kernel/tools/perf/arch/powerpc/util/header.c create mode 100644 kernel/tools/perf/arch/powerpc/util/skip-callchain-idx.c create mode 100644 kernel/tools/perf/arch/s390/Build create mode 100644 kernel/tools/perf/arch/s390/Makefile create mode 100644 kernel/tools/perf/arch/s390/util/Build create mode 100644 kernel/tools/perf/arch/s390/util/dwarf-regs.c create mode 100644 kernel/tools/perf/arch/s390/util/header.c create mode 100644 kernel/tools/perf/arch/s390/util/kvm-stat.c create mode 100644 kernel/tools/perf/arch/sh/Build create mode 100644 kernel/tools/perf/arch/sh/Makefile create mode 100644 kernel/tools/perf/arch/sh/util/Build create mode 100644 kernel/tools/perf/arch/sh/util/dwarf-regs.c create mode 100644 kernel/tools/perf/arch/sparc/Build create mode 100644 kernel/tools/perf/arch/sparc/Makefile create mode 100644 kernel/tools/perf/arch/sparc/util/Build create mode 100644 kernel/tools/perf/arch/sparc/util/dwarf-regs.c create mode 100644 kernel/tools/perf/arch/x86/Build create mode 100644 kernel/tools/perf/arch/x86/Makefile create mode 100644 kernel/tools/perf/arch/x86/include/perf_regs.h create mode 100644 kernel/tools/perf/arch/x86/tests/Build create mode 100644 kernel/tools/perf/arch/x86/tests/dwarf-unwind.c create mode 100644 kernel/tools/perf/arch/x86/tests/regs_load.S create mode 100644 kernel/tools/perf/arch/x86/util/Build create mode 100644 kernel/tools/perf/arch/x86/util/dwarf-regs.c create mode 100644 kernel/tools/perf/arch/x86/util/header.c create mode 100644 kernel/tools/perf/arch/x86/util/kvm-stat.c create mode 100644 kernel/tools/perf/arch/x86/util/tsc.c create mode 100644 kernel/tools/perf/arch/x86/util/tsc.h create mode 100644 kernel/tools/perf/arch/x86/util/unwind-libdw.c create mode 100644 kernel/tools/perf/arch/x86/util/unwind-libunwind.c create mode 100644 kernel/tools/perf/bench/Build create mode 100644 kernel/tools/perf/bench/bench.h create mode 100644 kernel/tools/perf/bench/futex-hash.c create mode 100644 kernel/tools/perf/bench/futex-requeue.c create mode 100644 kernel/tools/perf/bench/futex-wake.c create mode 100644 kernel/tools/perf/bench/futex.h create mode 100644 kernel/tools/perf/bench/mem-memcpy-arch.h create mode 100644 kernel/tools/perf/bench/mem-memcpy-x86-64-asm-def.h create mode 100644 kernel/tools/perf/bench/mem-memcpy-x86-64-asm.S create mode 100644 kernel/tools/perf/bench/mem-memcpy.c create mode 100644 kernel/tools/perf/bench/mem-memset-arch.h create mode 100644 kernel/tools/perf/bench/mem-memset-x86-64-asm-def.h create mode 100644 kernel/tools/perf/bench/mem-memset-x86-64-asm.S create mode 100644 kernel/tools/perf/bench/numa.c create mode 100644 kernel/tools/perf/bench/sched-messaging.c create mode 100644 kernel/tools/perf/bench/sched-pipe.c create mode 100644 kernel/tools/perf/builtin-annotate.c create mode 100644 kernel/tools/perf/builtin-bench.c create mode 100644 kernel/tools/perf/builtin-buildid-cache.c create mode 100644 kernel/tools/perf/builtin-buildid-list.c create mode 100644 kernel/tools/perf/builtin-data.c create mode 100644 kernel/tools/perf/builtin-diff.c create mode 100644 kernel/tools/perf/builtin-evlist.c create mode 100644 kernel/tools/perf/builtin-help.c create mode 100644 kernel/tools/perf/builtin-inject.c create mode 100644 kernel/tools/perf/builtin-kmem.c create mode 100644 kernel/tools/perf/builtin-kvm.c create mode 100644 kernel/tools/perf/builtin-list.c create mode 100644 kernel/tools/perf/builtin-lock.c create mode 100644 kernel/tools/perf/builtin-mem.c create mode 100644 kernel/tools/perf/builtin-probe.c create mode 100644 kernel/tools/perf/builtin-record.c create mode 100644 kernel/tools/perf/builtin-report.c create mode 100644 kernel/tools/perf/builtin-sched.c create mode 100644 kernel/tools/perf/builtin-script.c create mode 100644 kernel/tools/perf/builtin-stat.c create mode 100644 kernel/tools/perf/builtin-timechart.c create mode 100644 kernel/tools/perf/builtin-top.c create mode 100644 kernel/tools/perf/builtin-trace.c create mode 100644 kernel/tools/perf/builtin.h create mode 100644 kernel/tools/perf/command-list.txt create mode 100644 kernel/tools/perf/config/Makefile create mode 100644 kernel/tools/perf/config/Makefile.arch create mode 100644 kernel/tools/perf/config/utilities.mak create mode 100644 kernel/tools/perf/design.txt create mode 100644 kernel/tools/perf/perf-archive.sh create mode 100644 kernel/tools/perf/perf-completion.sh create mode 100644 kernel/tools/perf/perf-read-vdso.c create mode 100644 kernel/tools/perf/perf-sys.h create mode 100644 kernel/tools/perf/perf-with-kcore.sh create mode 100644 kernel/tools/perf/perf.c create mode 100644 kernel/tools/perf/perf.h create mode 100755 kernel/tools/perf/python/twatch.py create mode 100644 kernel/tools/perf/scripts/Build create mode 100644 kernel/tools/perf/scripts/perl/Perf-Trace-Util/Build create mode 100644 kernel/tools/perf/scripts/perl/Perf-Trace-Util/Context.c create mode 100644 kernel/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs create mode 100644 kernel/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL create mode 100644 kernel/tools/perf/scripts/perl/Perf-Trace-Util/README create mode 100644 kernel/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm create mode 100644 kernel/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm create mode 100644 kernel/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm create mode 100644 kernel/tools/perf/scripts/perl/Perf-Trace-Util/typemap create mode 100644 kernel/tools/perf/scripts/perl/bin/check-perf-trace-record create mode 100644 kernel/tools/perf/scripts/perl/bin/failed-syscalls-record create mode 100644 kernel/tools/perf/scripts/perl/bin/failed-syscalls-report create mode 100644 kernel/tools/perf/scripts/perl/bin/rw-by-file-record create mode 100644 kernel/tools/perf/scripts/perl/bin/rw-by-file-report create mode 100644 kernel/tools/perf/scripts/perl/bin/rw-by-pid-record create mode 100644 kernel/tools/perf/scripts/perl/bin/rw-by-pid-report create mode 100644 kernel/tools/perf/scripts/perl/bin/rwtop-record create mode 100644 kernel/tools/perf/scripts/perl/bin/rwtop-report create mode 100644 kernel/tools/perf/scripts/perl/bin/wakeup-latency-record create mode 100644 kernel/tools/perf/scripts/perl/bin/wakeup-latency-report create mode 100644 kernel/tools/perf/scripts/perl/check-perf-trace.pl create mode 100644 kernel/tools/perf/scripts/perl/failed-syscalls.pl create mode 100644 kernel/tools/perf/scripts/perl/rw-by-file.pl create mode 100644 kernel/tools/perf/scripts/perl/rw-by-pid.pl create mode 100644 kernel/tools/perf/scripts/perl/rwtop.pl create mode 100644 kernel/tools/perf/scripts/perl/wakeup-latency.pl create mode 100644 kernel/tools/perf/scripts/python/Perf-Trace-Util/Build create mode 100644 kernel/tools/perf/scripts/python/Perf-Trace-Util/Context.c create mode 100644 kernel/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py create mode 100755 kernel/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py create mode 100644 kernel/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py create mode 100644 kernel/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py create mode 100644 kernel/tools/perf/scripts/python/bin/event_analyzing_sample-record create mode 100644 kernel/tools/perf/scripts/python/bin/event_analyzing_sample-report create mode 100644 kernel/tools/perf/scripts/python/bin/export-to-postgresql-record create mode 100644 kernel/tools/perf/scripts/python/bin/export-to-postgresql-report create mode 100644 kernel/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record create mode 100644 kernel/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report create mode 100644 kernel/tools/perf/scripts/python/bin/futex-contention-record create mode 100644 kernel/tools/perf/scripts/python/bin/futex-contention-report create mode 100755 kernel/tools/perf/scripts/python/bin/net_dropmonitor-record create mode 100755 kernel/tools/perf/scripts/python/bin/net_dropmonitor-report create mode 100644 kernel/tools/perf/scripts/python/bin/netdev-times-record create mode 100644 kernel/tools/perf/scripts/python/bin/netdev-times-report create mode 100644 kernel/tools/perf/scripts/python/bin/sched-migration-record create mode 100644 kernel/tools/perf/scripts/python/bin/sched-migration-report create mode 100644 kernel/tools/perf/scripts/python/bin/sctop-record create mode 100644 kernel/tools/perf/scripts/python/bin/sctop-report create mode 100644 kernel/tools/perf/scripts/python/bin/syscall-counts-by-pid-record create mode 100644 kernel/tools/perf/scripts/python/bin/syscall-counts-by-pid-report create mode 100644 kernel/tools/perf/scripts/python/bin/syscall-counts-record create mode 100644 kernel/tools/perf/scripts/python/bin/syscall-counts-report create mode 100644 kernel/tools/perf/scripts/python/check-perf-trace.py create mode 100644 kernel/tools/perf/scripts/python/event_analyzing_sample.py create mode 100644 kernel/tools/perf/scripts/python/export-to-postgresql.py create mode 100644 kernel/tools/perf/scripts/python/failed-syscalls-by-pid.py create mode 100644 kernel/tools/perf/scripts/python/futex-contention.py create mode 100755 kernel/tools/perf/scripts/python/net_dropmonitor.py create mode 100644 kernel/tools/perf/scripts/python/netdev-times.py create mode 100644 kernel/tools/perf/scripts/python/sched-migration.py create mode 100644 kernel/tools/perf/scripts/python/sctop.py create mode 100644 kernel/tools/perf/scripts/python/syscall-counts-by-pid.py create mode 100644 kernel/tools/perf/scripts/python/syscall-counts.py create mode 100644 kernel/tools/perf/tests/Build create mode 100644 kernel/tools/perf/tests/attr.c create mode 100644 kernel/tools/perf/tests/attr.py create mode 100644 kernel/tools/perf/tests/attr/README create mode 100644 kernel/tools/perf/tests/attr/base-record create mode 100644 kernel/tools/perf/tests/attr/base-stat create mode 100644 kernel/tools/perf/tests/attr/test-record-C0 create mode 100644 kernel/tools/perf/tests/attr/test-record-basic create mode 100644 kernel/tools/perf/tests/attr/test-record-branch-any create mode 100644 kernel/tools/perf/tests/attr/test-record-branch-filter-any create mode 100644 kernel/tools/perf/tests/attr/test-record-branch-filter-any_call create mode 100644 kernel/tools/perf/tests/attr/test-record-branch-filter-any_ret create mode 100644 kernel/tools/perf/tests/attr/test-record-branch-filter-hv create mode 100644 kernel/tools/perf/tests/attr/test-record-branch-filter-ind_call create mode 100644 kernel/tools/perf/tests/attr/test-record-branch-filter-k create mode 100644 kernel/tools/perf/tests/attr/test-record-branch-filter-u create mode 100644 kernel/tools/perf/tests/attr/test-record-count create mode 100644 kernel/tools/perf/tests/attr/test-record-data create mode 100644 kernel/tools/perf/tests/attr/test-record-freq create mode 100644 kernel/tools/perf/tests/attr/test-record-graph-default create mode 100644 kernel/tools/perf/tests/attr/test-record-graph-dwarf create mode 100644 kernel/tools/perf/tests/attr/test-record-graph-fp create mode 100644 kernel/tools/perf/tests/attr/test-record-group create mode 100644 kernel/tools/perf/tests/attr/test-record-group-sampling create mode 100644 kernel/tools/perf/tests/attr/test-record-group1 create mode 100644 kernel/tools/perf/tests/attr/test-record-no-delay create mode 100644 kernel/tools/perf/tests/attr/test-record-no-inherit create mode 100644 kernel/tools/perf/tests/attr/test-record-no-samples create mode 100644 kernel/tools/perf/tests/attr/test-record-period create mode 100644 kernel/tools/perf/tests/attr/test-record-raw create mode 100644 kernel/tools/perf/tests/attr/test-stat-C0 create mode 100644 kernel/tools/perf/tests/attr/test-stat-basic create mode 100644 kernel/tools/perf/tests/attr/test-stat-default create mode 100644 kernel/tools/perf/tests/attr/test-stat-detailed-1 create mode 100644 kernel/tools/perf/tests/attr/test-stat-detailed-2 create mode 100644 kernel/tools/perf/tests/attr/test-stat-detailed-3 create mode 100644 kernel/tools/perf/tests/attr/test-stat-group create mode 100644 kernel/tools/perf/tests/attr/test-stat-group1 create mode 100644 kernel/tools/perf/tests/attr/test-stat-no-inherit create mode 100644 kernel/tools/perf/tests/bp_signal.c create mode 100644 kernel/tools/perf/tests/bp_signal_overflow.c create mode 100644 kernel/tools/perf/tests/builtin-test.c create mode 100644 kernel/tools/perf/tests/code-reading.c create mode 100644 kernel/tools/perf/tests/dso-data.c create mode 100644 kernel/tools/perf/tests/dwarf-unwind.c create mode 100644 kernel/tools/perf/tests/evsel-roundtrip-name.c create mode 100644 kernel/tools/perf/tests/evsel-tp-sched.c create mode 100644 kernel/tools/perf/tests/fdarray.c create mode 100644 kernel/tools/perf/tests/hists_common.c create mode 100644 kernel/tools/perf/tests/hists_common.h create mode 100644 kernel/tools/perf/tests/hists_cumulate.c create mode 100644 kernel/tools/perf/tests/hists_filter.c create mode 100644 kernel/tools/perf/tests/hists_link.c create mode 100644 kernel/tools/perf/tests/hists_output.c create mode 100644 kernel/tools/perf/tests/keep-tracking.c create mode 100644 kernel/tools/perf/tests/kmod-path.c create mode 100644 kernel/tools/perf/tests/make create mode 100644 kernel/tools/perf/tests/mmap-basic.c create mode 100644 kernel/tools/perf/tests/mmap-thread-lookup.c create mode 100644 kernel/tools/perf/tests/open-syscall-all-cpus.c create mode 100644 kernel/tools/perf/tests/open-syscall-tp-fields.c create mode 100644 kernel/tools/perf/tests/open-syscall.c create mode 100644 kernel/tools/perf/tests/parse-events.c create mode 100644 kernel/tools/perf/tests/parse-no-sample-id-all.c create mode 100644 kernel/tools/perf/tests/perf-record.c create mode 100755 kernel/tools/perf/tests/perf-targz-src-pkg create mode 100644 kernel/tools/perf/tests/perf-time-to-tsc.c create mode 100644 kernel/tools/perf/tests/pmu.c create mode 100644 kernel/tools/perf/tests/python-use.c create mode 100644 kernel/tools/perf/tests/rdpmc.c create mode 100644 kernel/tools/perf/tests/sample-parsing.c create mode 100644 kernel/tools/perf/tests/sw-clock.c create mode 100644 kernel/tools/perf/tests/switch-tracking.c create mode 100644 kernel/tools/perf/tests/task-exit.c create mode 100644 kernel/tools/perf/tests/tests.h create mode 100644 kernel/tools/perf/tests/thread-mg-share.c create mode 100644 kernel/tools/perf/tests/vmlinux-kallsyms.c create mode 100644 kernel/tools/perf/ui/Build create mode 100644 kernel/tools/perf/ui/browser.c create mode 100644 kernel/tools/perf/ui/browser.h create mode 100644 kernel/tools/perf/ui/browsers/Build create mode 100644 kernel/tools/perf/ui/browsers/annotate.c create mode 100644 kernel/tools/perf/ui/browsers/header.c create mode 100644 kernel/tools/perf/ui/browsers/hists.c create mode 100644 kernel/tools/perf/ui/browsers/map.c create mode 100644 kernel/tools/perf/ui/browsers/map.h create mode 100644 kernel/tools/perf/ui/browsers/scripts.c create mode 100644 kernel/tools/perf/ui/gtk/Build create mode 100644 kernel/tools/perf/ui/gtk/annotate.c create mode 100644 kernel/tools/perf/ui/gtk/browser.c create mode 100644 kernel/tools/perf/ui/gtk/gtk.h create mode 100644 kernel/tools/perf/ui/gtk/helpline.c create mode 100644 kernel/tools/perf/ui/gtk/hists.c create mode 100644 kernel/tools/perf/ui/gtk/progress.c create mode 100644 kernel/tools/perf/ui/gtk/setup.c create mode 100644 kernel/tools/perf/ui/gtk/util.c create mode 100644 kernel/tools/perf/ui/helpline.c create mode 100644 kernel/tools/perf/ui/helpline.h create mode 100644 kernel/tools/perf/ui/hist.c create mode 100644 kernel/tools/perf/ui/keysyms.h create mode 100644 kernel/tools/perf/ui/libslang.h create mode 100644 kernel/tools/perf/ui/progress.c create mode 100644 kernel/tools/perf/ui/progress.h create mode 100644 kernel/tools/perf/ui/setup.c create mode 100644 kernel/tools/perf/ui/stdio/hist.c create mode 100644 kernel/tools/perf/ui/tui/Build create mode 100644 kernel/tools/perf/ui/tui/helpline.c create mode 100644 kernel/tools/perf/ui/tui/progress.c create mode 100644 kernel/tools/perf/ui/tui/setup.c create mode 100644 kernel/tools/perf/ui/tui/tui.h create mode 100644 kernel/tools/perf/ui/tui/util.c create mode 100644 kernel/tools/perf/ui/ui.h create mode 100644 kernel/tools/perf/ui/util.c create mode 100644 kernel/tools/perf/ui/util.h create mode 100644 kernel/tools/perf/util/Build create mode 100755 kernel/tools/perf/util/PERF-VERSION-GEN create mode 100644 kernel/tools/perf/util/abspath.c create mode 100644 kernel/tools/perf/util/alias.c create mode 100644 kernel/tools/perf/util/annotate.c create mode 100644 kernel/tools/perf/util/annotate.h create mode 100644 kernel/tools/perf/util/bitmap.c create mode 100644 kernel/tools/perf/util/build-id.c create mode 100644 kernel/tools/perf/util/build-id.h create mode 100644 kernel/tools/perf/util/cache.h create mode 100644 kernel/tools/perf/util/callchain.c create mode 100644 kernel/tools/perf/util/callchain.h create mode 100644 kernel/tools/perf/util/cgroup.c create mode 100644 kernel/tools/perf/util/cgroup.h create mode 100644 kernel/tools/perf/util/cloexec.c create mode 100644 kernel/tools/perf/util/cloexec.h create mode 100644 kernel/tools/perf/util/color.c create mode 100644 kernel/tools/perf/util/color.h create mode 100644 kernel/tools/perf/util/comm.c create mode 100644 kernel/tools/perf/util/comm.h create mode 100644 kernel/tools/perf/util/config.c create mode 100644 kernel/tools/perf/util/cpumap.c create mode 100644 kernel/tools/perf/util/cpumap.h create mode 100644 kernel/tools/perf/util/ctype.c create mode 100644 kernel/tools/perf/util/data-convert-bt.c create mode 100644 kernel/tools/perf/util/data-convert-bt.h create mode 100644 kernel/tools/perf/util/data.c create mode 100644 kernel/tools/perf/util/data.h create mode 100644 kernel/tools/perf/util/db-export.c create mode 100644 kernel/tools/perf/util/db-export.h create mode 100644 kernel/tools/perf/util/debug.c create mode 100644 kernel/tools/perf/util/debug.h create mode 100644 kernel/tools/perf/util/dso.c create mode 100644 kernel/tools/perf/util/dso.h create mode 100644 kernel/tools/perf/util/dwarf-aux.c create mode 100644 kernel/tools/perf/util/dwarf-aux.h create mode 100644 kernel/tools/perf/util/environment.c create mode 100644 kernel/tools/perf/util/event.c create mode 100644 kernel/tools/perf/util/event.h create mode 100644 kernel/tools/perf/util/evlist.c create mode 100644 kernel/tools/perf/util/evlist.h create mode 100644 kernel/tools/perf/util/evsel.c create mode 100644 kernel/tools/perf/util/evsel.h create mode 100644 kernel/tools/perf/util/exec_cmd.c create mode 100644 kernel/tools/perf/util/exec_cmd.h create mode 100644 kernel/tools/perf/util/find-vdso-map.c create mode 100755 kernel/tools/perf/util/generate-cmdlist.sh create mode 100644 kernel/tools/perf/util/header.c create mode 100644 kernel/tools/perf/util/header.h create mode 100644 kernel/tools/perf/util/help.c create mode 100644 kernel/tools/perf/util/help.h create mode 100644 kernel/tools/perf/util/hist.c create mode 100644 kernel/tools/perf/util/hist.h create mode 100644 kernel/tools/perf/util/include/asm/alternative-asm.h create mode 100644 kernel/tools/perf/util/include/asm/asm-offsets.h create mode 100644 kernel/tools/perf/util/include/asm/byteorder.h create mode 100644 kernel/tools/perf/util/include/asm/cpufeature.h create mode 100644 kernel/tools/perf/util/include/asm/dwarf2.h create mode 100644 kernel/tools/perf/util/include/asm/swab.h create mode 100644 kernel/tools/perf/util/include/asm/system.h create mode 100644 kernel/tools/perf/util/include/asm/uaccess.h create mode 100644 kernel/tools/perf/util/include/asm/unistd_32.h create mode 100644 kernel/tools/perf/util/include/asm/unistd_64.h create mode 100644 kernel/tools/perf/util/include/dwarf-regs.h create mode 100644 kernel/tools/perf/util/include/linux/bitmap.h create mode 100644 kernel/tools/perf/util/include/linux/const.h create mode 100644 kernel/tools/perf/util/include/linux/ctype.h create mode 100644 kernel/tools/perf/util/include/linux/kernel.h create mode 100644 kernel/tools/perf/util/include/linux/linkage.h create mode 100644 kernel/tools/perf/util/include/linux/list.h create mode 100644 kernel/tools/perf/util/include/linux/poison.h create mode 100644 kernel/tools/perf/util/include/linux/rbtree.h create mode 100644 kernel/tools/perf/util/include/linux/rbtree_augmented.h create mode 100644 kernel/tools/perf/util/include/linux/string.h create mode 100644 kernel/tools/perf/util/intlist.c create mode 100644 kernel/tools/perf/util/intlist.h create mode 100644 kernel/tools/perf/util/kvm-stat.h create mode 100644 kernel/tools/perf/util/levenshtein.c create mode 100644 kernel/tools/perf/util/levenshtein.h create mode 100644 kernel/tools/perf/util/lzma.c create mode 100644 kernel/tools/perf/util/machine.c create mode 100644 kernel/tools/perf/util/machine.h create mode 100644 kernel/tools/perf/util/map.c create mode 100644 kernel/tools/perf/util/map.h create mode 100644 kernel/tools/perf/util/ordered-events.c create mode 100644 kernel/tools/perf/util/ordered-events.h create mode 100644 kernel/tools/perf/util/pager.c create mode 100644 kernel/tools/perf/util/parse-events.c create mode 100644 kernel/tools/perf/util/parse-events.h create mode 100644 kernel/tools/perf/util/parse-events.l create mode 100644 kernel/tools/perf/util/parse-events.y create mode 100644 kernel/tools/perf/util/parse-options.c create mode 100644 kernel/tools/perf/util/parse-options.h create mode 100644 kernel/tools/perf/util/path.c create mode 100644 kernel/tools/perf/util/perf_regs.c create mode 100644 kernel/tools/perf/util/perf_regs.h create mode 100644 kernel/tools/perf/util/pmu.c create mode 100644 kernel/tools/perf/util/pmu.h create mode 100644 kernel/tools/perf/util/pmu.l create mode 100644 kernel/tools/perf/util/pmu.y create mode 100644 kernel/tools/perf/util/probe-event.c create mode 100644 kernel/tools/perf/util/probe-event.h create mode 100644 kernel/tools/perf/util/probe-finder.c create mode 100644 kernel/tools/perf/util/probe-finder.h create mode 100644 kernel/tools/perf/util/pstack.c create mode 100644 kernel/tools/perf/util/pstack.h create mode 100644 kernel/tools/perf/util/python-ext-sources create mode 100644 kernel/tools/perf/util/python.c create mode 100644 kernel/tools/perf/util/quote.c create mode 100644 kernel/tools/perf/util/quote.h create mode 100644 kernel/tools/perf/util/rblist.c create mode 100644 kernel/tools/perf/util/rblist.h create mode 100644 kernel/tools/perf/util/record.c create mode 100644 kernel/tools/perf/util/run-command.c create mode 100644 kernel/tools/perf/util/run-command.h create mode 100644 kernel/tools/perf/util/scripting-engines/Build create mode 100644 kernel/tools/perf/util/scripting-engines/trace-event-perl.c create mode 100644 kernel/tools/perf/util/scripting-engines/trace-event-python.c create mode 100644 kernel/tools/perf/util/session.c create mode 100644 kernel/tools/perf/util/session.h create mode 100644 kernel/tools/perf/util/setup.py create mode 100644 kernel/tools/perf/util/sigchain.c create mode 100644 kernel/tools/perf/util/sigchain.h create mode 100644 kernel/tools/perf/util/sort.c create mode 100644 kernel/tools/perf/util/sort.h create mode 100644 kernel/tools/perf/util/srcline.c create mode 100644 kernel/tools/perf/util/stat.c create mode 100644 kernel/tools/perf/util/stat.h create mode 100644 kernel/tools/perf/util/strbuf.c create mode 100644 kernel/tools/perf/util/strbuf.h create mode 100644 kernel/tools/perf/util/strfilter.c create mode 100644 kernel/tools/perf/util/strfilter.h create mode 100644 kernel/tools/perf/util/string.c create mode 100644 kernel/tools/perf/util/strlist.c create mode 100644 kernel/tools/perf/util/strlist.h create mode 100644 kernel/tools/perf/util/svghelper.c create mode 100644 kernel/tools/perf/util/svghelper.h create mode 100644 kernel/tools/perf/util/symbol-elf.c create mode 100644 kernel/tools/perf/util/symbol-minimal.c create mode 100644 kernel/tools/perf/util/symbol.c create mode 100644 kernel/tools/perf/util/symbol.h create mode 100644 kernel/tools/perf/util/target.c create mode 100644 kernel/tools/perf/util/target.h create mode 100644 kernel/tools/perf/util/thread-stack.c create mode 100644 kernel/tools/perf/util/thread-stack.h create mode 100644 kernel/tools/perf/util/thread.c create mode 100644 kernel/tools/perf/util/thread.h create mode 100644 kernel/tools/perf/util/thread_map.c create mode 100644 kernel/tools/perf/util/thread_map.h create mode 100644 kernel/tools/perf/util/tool.h create mode 100644 kernel/tools/perf/util/top.c create mode 100644 kernel/tools/perf/util/top.h create mode 100644 kernel/tools/perf/util/trace-event-info.c create mode 100644 kernel/tools/perf/util/trace-event-parse.c create mode 100644 kernel/tools/perf/util/trace-event-read.c create mode 100644 kernel/tools/perf/util/trace-event-scripting.c create mode 100644 kernel/tools/perf/util/trace-event.c create mode 100644 kernel/tools/perf/util/trace-event.h create mode 100644 kernel/tools/perf/util/tsc.c create mode 100644 kernel/tools/perf/util/tsc.h create mode 100644 kernel/tools/perf/util/unwind-libdw.c create mode 100644 kernel/tools/perf/util/unwind-libdw.h create mode 100644 kernel/tools/perf/util/unwind-libunwind.c create mode 100644 kernel/tools/perf/util/unwind.h create mode 100644 kernel/tools/perf/util/usage.c create mode 100644 kernel/tools/perf/util/util.c create mode 100644 kernel/tools/perf/util/util.h create mode 100644 kernel/tools/perf/util/values.c create mode 100644 kernel/tools/perf/util/values.h create mode 100644 kernel/tools/perf/util/vdso.c create mode 100644 kernel/tools/perf/util/vdso.h create mode 100644 kernel/tools/perf/util/wrapper.c create mode 100644 kernel/tools/perf/util/xyarray.c create mode 100644 kernel/tools/perf/util/xyarray.h create mode 100644 kernel/tools/perf/util/zlib.c create mode 100644 kernel/tools/power/acpi/Makefile create mode 100644 kernel/tools/power/acpi/common/cmfsize.c create mode 100644 kernel/tools/power/acpi/common/getopt.c create mode 100644 kernel/tools/power/acpi/man/acpidump.8 create mode 100644 kernel/tools/power/acpi/os_specific/service_layers/oslibcfs.c create mode 100644 kernel/tools/power/acpi/os_specific/service_layers/oslinuxtbl.c create mode 100644 kernel/tools/power/acpi/os_specific/service_layers/osunixdir.c create mode 100644 kernel/tools/power/acpi/os_specific/service_layers/osunixmap.c create mode 100644 kernel/tools/power/acpi/os_specific/service_layers/osunixxf.c create mode 100644 kernel/tools/power/acpi/tools/acpidump/acpidump.h create mode 100644 kernel/tools/power/acpi/tools/acpidump/apdump.c create mode 100644 kernel/tools/power/acpi/tools/acpidump/apfiles.c create mode 100644 kernel/tools/power/acpi/tools/acpidump/apmain.c create mode 100644 kernel/tools/power/acpi/tools/ec/Makefile create mode 100644 kernel/tools/power/acpi/tools/ec/ec_access.c create mode 100644 kernel/tools/power/cpupower/.gitignore create mode 100644 kernel/tools/power/cpupower/Makefile create mode 100644 kernel/tools/power/cpupower/README create mode 100644 kernel/tools/power/cpupower/ToDo create mode 100644 kernel/tools/power/cpupower/bench/Makefile create mode 100644 kernel/tools/power/cpupower/bench/README-BENCH create mode 100644 kernel/tools/power/cpupower/bench/benchmark.c create mode 100644 kernel/tools/power/cpupower/bench/benchmark.h create mode 100644 kernel/tools/power/cpupower/bench/config.h create mode 100644 kernel/tools/power/cpupower/bench/cpufreq-bench_plot.sh create mode 100644 kernel/tools/power/cpupower/bench/cpufreq-bench_script.sh create mode 100644 kernel/tools/power/cpupower/bench/example.cfg create mode 100644 kernel/tools/power/cpupower/bench/main.c create mode 100644 kernel/tools/power/cpupower/bench/parse.c create mode 100644 kernel/tools/power/cpupower/bench/parse.h create mode 100644 kernel/tools/power/cpupower/bench/system.c create mode 100644 kernel/tools/power/cpupower/bench/system.h create mode 100644 kernel/tools/power/cpupower/debug/i386/Makefile create mode 100644 kernel/tools/power/cpupower/debug/i386/centrino-decode.c create mode 100644 kernel/tools/power/cpupower/debug/i386/dump_psb.c create mode 100644 kernel/tools/power/cpupower/debug/i386/intel_gsic.c create mode 100644 kernel/tools/power/cpupower/debug/i386/powernow-k8-decode.c create mode 100644 kernel/tools/power/cpupower/debug/kernel/Makefile create mode 100644 kernel/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c create mode 100644 kernel/tools/power/cpupower/debug/x86_64/Makefile create mode 100644 kernel/tools/power/cpupower/lib/cpufreq.c create mode 100644 kernel/tools/power/cpupower/lib/cpufreq.h create mode 100644 kernel/tools/power/cpupower/lib/sysfs.c create mode 100644 kernel/tools/power/cpupower/lib/sysfs.h create mode 100644 kernel/tools/power/cpupower/man/cpupower-frequency-info.1 create mode 100644 kernel/tools/power/cpupower/man/cpupower-frequency-set.1 create mode 100644 kernel/tools/power/cpupower/man/cpupower-idle-info.1 create mode 100644 kernel/tools/power/cpupower/man/cpupower-idle-set.1 create mode 100644 kernel/tools/power/cpupower/man/cpupower-info.1 create mode 100644 kernel/tools/power/cpupower/man/cpupower-monitor.1 create mode 100644 kernel/tools/power/cpupower/man/cpupower-set.1 create mode 100644 kernel/tools/power/cpupower/man/cpupower.1 create mode 100644 kernel/tools/power/cpupower/po/cs.po create mode 100644 kernel/tools/power/cpupower/po/de.po create mode 100644 kernel/tools/power/cpupower/po/fr.po create mode 100644 kernel/tools/power/cpupower/po/it.po create mode 100644 kernel/tools/power/cpupower/po/pt.po create mode 100644 kernel/tools/power/cpupower/utils/builtin.h create mode 100644 kernel/tools/power/cpupower/utils/cpufreq-info.c create mode 100644 kernel/tools/power/cpupower/utils/cpufreq-set.c create mode 100644 kernel/tools/power/cpupower/utils/cpuidle-info.c create mode 100644 kernel/tools/power/cpupower/utils/cpuidle-set.c create mode 100644 kernel/tools/power/cpupower/utils/cpupower-info.c create mode 100644 kernel/tools/power/cpupower/utils/cpupower-set.c create mode 100644 kernel/tools/power/cpupower/utils/cpupower.c create mode 100644 kernel/tools/power/cpupower/utils/helpers/amd.c create mode 100644 kernel/tools/power/cpupower/utils/helpers/bitmask.c create mode 100644 kernel/tools/power/cpupower/utils/helpers/bitmask.h create mode 100644 kernel/tools/power/cpupower/utils/helpers/cpuid.c create mode 100644 kernel/tools/power/cpupower/utils/helpers/helpers.h create mode 100644 kernel/tools/power/cpupower/utils/helpers/misc.c create mode 100644 kernel/tools/power/cpupower/utils/helpers/msr.c create mode 100644 kernel/tools/power/cpupower/utils/helpers/pci.c create mode 100644 kernel/tools/power/cpupower/utils/helpers/sysfs.c create mode 100644 kernel/tools/power/cpupower/utils/helpers/sysfs.h create mode 100644 kernel/tools/power/cpupower/utils/helpers/topology.c create mode 100644 kernel/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c create mode 100644 kernel/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c create mode 100644 kernel/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c create mode 100644 kernel/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h create mode 100644 kernel/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c create mode 100644 kernel/tools/power/cpupower/utils/idle_monitor/idle_monitors.def create mode 100644 kernel/tools/power/cpupower/utils/idle_monitor/idle_monitors.h create mode 100644 kernel/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c create mode 100644 kernel/tools/power/cpupower/utils/idle_monitor/nhm_idle.c create mode 100644 kernel/tools/power/cpupower/utils/idle_monitor/snb_idle.c create mode 100755 kernel/tools/power/cpupower/utils/version-gen.sh create mode 100644 kernel/tools/power/x86/turbostat/.gitignore create mode 100644 kernel/tools/power/x86/turbostat/Makefile create mode 100644 kernel/tools/power/x86/turbostat/turbostat.8 create mode 100644 kernel/tools/power/x86/turbostat/turbostat.c create mode 100644 kernel/tools/power/x86/x86_energy_perf_policy/Makefile create mode 100644 kernel/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8 create mode 100644 kernel/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c create mode 100644 kernel/tools/scripts/Makefile.include create mode 100644 kernel/tools/testing/fault-injection/failcmd.sh create mode 100755 kernel/tools/testing/ktest/compare-ktest-sample.pl create mode 100644 kernel/tools/testing/ktest/examples/README create mode 100644 kernel/tools/testing/ktest/examples/crosstests.conf create mode 100644 kernel/tools/testing/ktest/examples/include/bisect.conf create mode 100644 kernel/tools/testing/ktest/examples/include/defaults.conf create mode 100644 kernel/tools/testing/ktest/examples/include/min-config.conf create mode 100644 kernel/tools/testing/ktest/examples/include/patchcheck.conf create mode 100644 kernel/tools/testing/ktest/examples/include/tests.conf create mode 100644 kernel/tools/testing/ktest/examples/kvm.conf create mode 100644 kernel/tools/testing/ktest/examples/snowball.conf create mode 100644 kernel/tools/testing/ktest/examples/test.conf create mode 100755 kernel/tools/testing/ktest/ktest.pl create mode 100644 kernel/tools/testing/ktest/sample.conf create mode 100644 kernel/tools/testing/selftests/Makefile create mode 100644 kernel/tools/testing/selftests/breakpoints/Makefile create mode 100644 kernel/tools/testing/selftests/breakpoints/breakpoint_test.c create mode 100644 kernel/tools/testing/selftests/cpu-hotplug/Makefile create mode 100755 kernel/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh create mode 100644 kernel/tools/testing/selftests/efivarfs/Makefile create mode 100644 kernel/tools/testing/selftests/efivarfs/create-read.c create mode 100755 kernel/tools/testing/selftests/efivarfs/efivarfs.sh create mode 100644 kernel/tools/testing/selftests/efivarfs/open-unlink.c create mode 100644 kernel/tools/testing/selftests/exec/.gitignore create mode 100644 kernel/tools/testing/selftests/exec/Makefile create mode 100644 kernel/tools/testing/selftests/exec/execveat.c create mode 100644 kernel/tools/testing/selftests/firmware/Makefile create mode 100755 kernel/tools/testing/selftests/firmware/fw_filesystem.sh create mode 100755 kernel/tools/testing/selftests/firmware/fw_userhelper.sh create mode 100644 kernel/tools/testing/selftests/ftrace/Makefile create mode 100644 kernel/tools/testing/selftests/ftrace/README create mode 100755 kernel/tools/testing/selftests/ftrace/ftracetest create mode 100644 kernel/tools/testing/selftests/ftrace/samples/fail.tc create mode 100644 kernel/tools/testing/selftests/ftrace/samples/pass.tc create mode 100644 kernel/tools/testing/selftests/ftrace/samples/unresolved.tc create mode 100644 kernel/tools/testing/selftests/ftrace/samples/unsupported.tc create mode 100644 kernel/tools/testing/selftests/ftrace/samples/untested.tc create mode 100644 kernel/tools/testing/selftests/ftrace/samples/xfail.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/00basic/basic1.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/00basic/basic2.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/00basic/basic3.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/00basic/basic4.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/event/event-enable.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/functions create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/kprobe/add_and_remove.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_ftrace.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc create mode 100644 kernel/tools/testing/selftests/ftrace/test.d/template create mode 100755 kernel/tools/testing/selftests/gen_kselftest_tar.sh create mode 100644 kernel/tools/testing/selftests/ipc/Makefile create mode 100644 kernel/tools/testing/selftests/ipc/msgque.c create mode 100644 kernel/tools/testing/selftests/kcmp/.gitignore create mode 100644 kernel/tools/testing/selftests/kcmp/Makefile create mode 100644 kernel/tools/testing/selftests/kcmp/kcmp_test.c create mode 100644 kernel/tools/testing/selftests/kselftest.h create mode 100755 kernel/tools/testing/selftests/kselftest_install.sh create mode 100644 kernel/tools/testing/selftests/lib.mk create mode 100644 kernel/tools/testing/selftests/memfd/.gitignore create mode 100644 kernel/tools/testing/selftests/memfd/Makefile create mode 100644 kernel/tools/testing/selftests/memfd/fuse_mnt.c create mode 100644 kernel/tools/testing/selftests/memfd/fuse_test.c create mode 100644 kernel/tools/testing/selftests/memfd/memfd_test.c create mode 100644 kernel/tools/testing/selftests/memfd/run_fuse_test.sh create mode 100644 kernel/tools/testing/selftests/memory-hotplug/Makefile create mode 100755 kernel/tools/testing/selftests/memory-hotplug/mem-on-off-test.sh create mode 100644 kernel/tools/testing/selftests/mount/.gitignore create mode 100644 kernel/tools/testing/selftests/mount/Makefile create mode 100644 kernel/tools/testing/selftests/mount/unprivileged-remount-test.c create mode 100644 kernel/tools/testing/selftests/mqueue/.gitignore create mode 100644 kernel/tools/testing/selftests/mqueue/Makefile create mode 100644 kernel/tools/testing/selftests/mqueue/mq_open_tests.c create mode 100644 kernel/tools/testing/selftests/mqueue/mq_perf_tests.c create mode 100644 kernel/tools/testing/selftests/net/.gitignore create mode 100644 kernel/tools/testing/selftests/net/Makefile create mode 100644 kernel/tools/testing/selftests/net/psock_fanout.c create mode 100644 kernel/tools/testing/selftests/net/psock_lib.h create mode 100644 kernel/tools/testing/selftests/net/psock_tpacket.c create mode 100755 kernel/tools/testing/selftests/net/run_afpackettests create mode 100755 kernel/tools/testing/selftests/net/run_netsocktests create mode 100644 kernel/tools/testing/selftests/net/socket.c create mode 100755 kernel/tools/testing/selftests/net/test_bpf.sh create mode 100644 kernel/tools/testing/selftests/powerpc/Makefile create mode 100644 kernel/tools/testing/selftests/powerpc/copyloops/.gitignore create mode 100644 kernel/tools/testing/selftests/powerpc/copyloops/Makefile create mode 100644 kernel/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h create mode 100644 kernel/tools/testing/selftests/powerpc/copyloops/asm/processor.h create mode 120000 kernel/tools/testing/selftests/powerpc/copyloops/copyuser_64.S create mode 120000 kernel/tools/testing/selftests/powerpc/copyloops/copyuser_power7.S create mode 120000 kernel/tools/testing/selftests/powerpc/copyloops/memcpy_64.S create mode 120000 kernel/tools/testing/selftests/powerpc/copyloops/memcpy_power7.S create mode 100644 kernel/tools/testing/selftests/powerpc/copyloops/validate.c create mode 100644 kernel/tools/testing/selftests/powerpc/harness.c create mode 100644 kernel/tools/testing/selftests/powerpc/mm/.gitignore create mode 100644 kernel/tools/testing/selftests/powerpc/mm/Makefile create mode 100644 kernel/tools/testing/selftests/powerpc/mm/hugetlb_vs_thp_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/mm/subpage_prot.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/.gitignore create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/Makefile create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/count_instructions.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/.gitignore create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/Makefile create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/busy_loop.S create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/ebb.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/ebb.h create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/reg.h create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/trace.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/ebb/trace.h create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/event.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/event.h create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/l3_bank_test.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/lib.c create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/lib.h create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/loop.S create mode 100644 kernel/tools/testing/selftests/powerpc/pmu/per_event_excludes.c create mode 100644 kernel/tools/testing/selftests/powerpc/primitives/.gitignore create mode 100644 kernel/tools/testing/selftests/powerpc/primitives/Makefile create mode 120000 kernel/tools/testing/selftests/powerpc/primitives/asm/asm-compat.h create mode 100644 kernel/tools/testing/selftests/powerpc/primitives/asm/ppc-opcode.h create mode 100644 kernel/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c create mode 120000 kernel/tools/testing/selftests/powerpc/primitives/word-at-a-time.h create mode 100644 kernel/tools/testing/selftests/powerpc/stringloops/.gitignore create mode 100644 kernel/tools/testing/selftests/powerpc/stringloops/Makefile create mode 100644 kernel/tools/testing/selftests/powerpc/stringloops/asm/ppc_asm.h create mode 100644 kernel/tools/testing/selftests/powerpc/stringloops/memcmp.c create mode 120000 kernel/tools/testing/selftests/powerpc/stringloops/memcmp_64.S create mode 100644 kernel/tools/testing/selftests/powerpc/subunit.h create mode 100644 kernel/tools/testing/selftests/powerpc/switch_endian/.gitignore create mode 100644 kernel/tools/testing/selftests/powerpc/switch_endian/Makefile create mode 100644 kernel/tools/testing/selftests/powerpc/switch_endian/check.S create mode 100644 kernel/tools/testing/selftests/powerpc/switch_endian/common.h create mode 100644 kernel/tools/testing/selftests/powerpc/switch_endian/switch_endian_test.S create mode 100644 kernel/tools/testing/selftests/powerpc/tm/.gitignore create mode 100644 kernel/tools/testing/selftests/powerpc/tm/Makefile create mode 100644 kernel/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c create mode 100644 kernel/tools/testing/selftests/powerpc/tm/tm-syscall-asm.S create mode 100644 kernel/tools/testing/selftests/powerpc/tm/tm-syscall.c create mode 100644 kernel/tools/testing/selftests/powerpc/utils.h create mode 100644 kernel/tools/testing/selftests/powerpc/vphn/.gitignore create mode 100644 kernel/tools/testing/selftests/powerpc/vphn/Makefile create mode 100644 kernel/tools/testing/selftests/powerpc/vphn/test-vphn.c create mode 120000 kernel/tools/testing/selftests/powerpc/vphn/vphn.c create mode 120000 kernel/tools/testing/selftests/powerpc/vphn/vphn.h create mode 100644 kernel/tools/testing/selftests/ptrace/Makefile create mode 100644 kernel/tools/testing/selftests/ptrace/peeksiginfo.c create mode 100644 kernel/tools/testing/selftests/rcutorture/.gitignore create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/config2frag.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/configNR_CPUS.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/configcheck.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/configinit.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/cpus2use.sh create mode 100644 kernel/tools/testing/selftests/rcutorture/bin/functions.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/kvm-build.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/kvm.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/parse-build.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/parse-console.sh create mode 100755 kernel/tools/testing/selftests/rcutorture/bin/parse-torture.sh create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/lock/BUSTED create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/lock/BUSTED.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/lock/CFLIST create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/lock/CFcommon create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/lock/LOCK01 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/lock/LOCK02 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/lock/LOCK02.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/lock/LOCK03 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/lock/LOCK03.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/lock/LOCK04 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/lock/LOCK04.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/lock/ver_functions.sh create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/BUSTED create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/BUSTED.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/CFLIST create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/CFcommon create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/SRCU-N create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/SRCU-N.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TASKS01 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TASKS01.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TASKS02 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TASKS02.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TASKS03 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TASKS03.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TINY01 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TINY02 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TINY02.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE01 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE02 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE02-T create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE03 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE04 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE04.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE05 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE05.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE06 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE06.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE07 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE07.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE08 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE08-T create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/TREE09 create mode 100644 kernel/tools/testing/selftests/rcutorture/configs/rcu/ver_functions.sh create mode 100644 kernel/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt create mode 100644 kernel/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt create mode 100644 kernel/tools/testing/selftests/rcutorture/doc/initrd.txt create mode 100644 kernel/tools/testing/selftests/rcutorture/doc/rcu-test-image.txt create mode 100644 kernel/tools/testing/selftests/size/.gitignore create mode 100644 kernel/tools/testing/selftests/size/Makefile create mode 100644 kernel/tools/testing/selftests/size/get_size.c create mode 100644 kernel/tools/testing/selftests/sysctl/Makefile create mode 100644 kernel/tools/testing/selftests/sysctl/common_tests create mode 100755 kernel/tools/testing/selftests/sysctl/run_numerictests create mode 100755 kernel/tools/testing/selftests/sysctl/run_stringtests create mode 100644 kernel/tools/testing/selftests/timers/Makefile create mode 100644 kernel/tools/testing/selftests/timers/alarmtimer-suspend.c create mode 100644 kernel/tools/testing/selftests/timers/change_skew.c create mode 100644 kernel/tools/testing/selftests/timers/clocksource-switch.c create mode 100644 kernel/tools/testing/selftests/timers/inconsistency-check.c create mode 100644 kernel/tools/testing/selftests/timers/leap-a-day.c create mode 100644 kernel/tools/testing/selftests/timers/leapcrash.c create mode 100644 kernel/tools/testing/selftests/timers/mqueue-lat.c create mode 100644 kernel/tools/testing/selftests/timers/nanosleep.c create mode 100644 kernel/tools/testing/selftests/timers/nsleep-lat.c create mode 100644 kernel/tools/testing/selftests/timers/posix_timers.c create mode 100644 kernel/tools/testing/selftests/timers/raw_skew.c create mode 100644 kernel/tools/testing/selftests/timers/rtctest.c create mode 100644 kernel/tools/testing/selftests/timers/set-2038.c create mode 100644 kernel/tools/testing/selftests/timers/set-tai.c create mode 100644 kernel/tools/testing/selftests/timers/set-timer-lat.c create mode 100644 kernel/tools/testing/selftests/timers/skew_consistency.c create mode 100644 kernel/tools/testing/selftests/timers/threadtest.c create mode 100644 kernel/tools/testing/selftests/timers/valid-adjtimex.c create mode 100644 kernel/tools/testing/selftests/user/Makefile create mode 100755 kernel/tools/testing/selftests/user/test_user_copy.sh create mode 100644 kernel/tools/testing/selftests/vm/.gitignore create mode 100644 kernel/tools/testing/selftests/vm/Makefile create mode 100644 kernel/tools/testing/selftests/vm/hugepage-mmap.c create mode 100644 kernel/tools/testing/selftests/vm/hugepage-shm.c create mode 100644 kernel/tools/testing/selftests/vm/hugetlbfstest.c create mode 100644 kernel/tools/testing/selftests/vm/map_hugetlb.c create mode 100755 kernel/tools/testing/selftests/vm/run_vmtests create mode 100644 kernel/tools/testing/selftests/vm/thuge-gen.c create mode 100644 kernel/tools/testing/selftests/vm/transhuge-stress.c create mode 100644 kernel/tools/testing/selftests/x86/.gitignore create mode 100644 kernel/tools/testing/selftests/x86/Makefile create mode 100755 kernel/tools/testing/selftests/x86/check_cc.sh create mode 100644 kernel/tools/testing/selftests/x86/entry_from_vm86.c create mode 100644 kernel/tools/testing/selftests/x86/sigreturn.c create mode 100644 kernel/tools/testing/selftests/x86/single_step_syscall.c create mode 100644 kernel/tools/testing/selftests/x86/trivial_32bit_program.c create mode 100644 kernel/tools/testing/selftests/x86/trivial_64bit_program.c create mode 100644 kernel/tools/thermal/tmon/.gitignore create mode 100644 kernel/tools/thermal/tmon/Makefile create mode 100644 kernel/tools/thermal/tmon/README create mode 100644 kernel/tools/thermal/tmon/pid.c create mode 100644 kernel/tools/thermal/tmon/sysfs.c create mode 100644 kernel/tools/thermal/tmon/tmon.8 create mode 100644 kernel/tools/thermal/tmon/tmon.c create mode 100644 kernel/tools/thermal/tmon/tmon.h create mode 100644 kernel/tools/thermal/tmon/tui.c create mode 100755 kernel/tools/time/udelay_test.sh create mode 100644 kernel/tools/usb/Makefile create mode 100644 kernel/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c create mode 100644 kernel/tools/usb/ffs-aio-example/multibuff/host_app/Makefile create mode 100644 kernel/tools/usb/ffs-aio-example/multibuff/host_app/test.c create mode 100644 kernel/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c create mode 100644 kernel/tools/usb/ffs-aio-example/simple/host_app/Makefile create mode 100644 kernel/tools/usb/ffs-aio-example/simple/host_app/test.c create mode 100644 kernel/tools/usb/ffs-test.c create mode 100644 kernel/tools/usb/hcd-tests.sh create mode 100644 kernel/tools/usb/testusb.c create mode 100644 kernel/tools/usb/usbip/.gitignore create mode 100644 kernel/tools/usb/usbip/AUTHORS create mode 100644 kernel/tools/usb/usbip/COPYING create mode 100644 kernel/tools/usb/usbip/INSTALL create mode 100644 kernel/tools/usb/usbip/Makefile.am create mode 100644 kernel/tools/usb/usbip/README create mode 100755 kernel/tools/usb/usbip/autogen.sh create mode 100755 kernel/tools/usb/usbip/cleanup.sh create mode 100644 kernel/tools/usb/usbip/configure.ac create mode 100644 kernel/tools/usb/usbip/doc/usbip.8 create mode 100644 kernel/tools/usb/usbip/doc/usbipd.8 create mode 100644 kernel/tools/usb/usbip/libsrc/Makefile.am create mode 100644 kernel/tools/usb/usbip/libsrc/list.h create mode 100644 kernel/tools/usb/usbip/libsrc/names.c create mode 100644 kernel/tools/usb/usbip/libsrc/names.h create mode 100644 kernel/tools/usb/usbip/libsrc/sysfs_utils.c create mode 100644 kernel/tools/usb/usbip/libsrc/sysfs_utils.h create mode 100644 kernel/tools/usb/usbip/libsrc/usbip_common.c create mode 100644 kernel/tools/usb/usbip/libsrc/usbip_common.h create mode 100644 kernel/tools/usb/usbip/libsrc/usbip_host_driver.c create mode 100644 kernel/tools/usb/usbip/libsrc/usbip_host_driver.h create mode 100644 kernel/tools/usb/usbip/libsrc/vhci_driver.c create mode 100644 kernel/tools/usb/usbip/libsrc/vhci_driver.h create mode 100644 kernel/tools/usb/usbip/src/Makefile.am create mode 100644 kernel/tools/usb/usbip/src/usbip.c create mode 100644 kernel/tools/usb/usbip/src/usbip.h create mode 100644 kernel/tools/usb/usbip/src/usbip_attach.c create mode 100644 kernel/tools/usb/usbip/src/usbip_bind.c create mode 100644 kernel/tools/usb/usbip/src/usbip_detach.c create mode 100644 kernel/tools/usb/usbip/src/usbip_list.c create mode 100644 kernel/tools/usb/usbip/src/usbip_network.c create mode 100644 kernel/tools/usb/usbip/src/usbip_network.h create mode 100644 kernel/tools/usb/usbip/src/usbip_port.c create mode 100644 kernel/tools/usb/usbip/src/usbip_unbind.c create mode 100644 kernel/tools/usb/usbip/src/usbipd.c create mode 100644 kernel/tools/usb/usbip/src/utils.c create mode 100644 kernel/tools/usb/usbip/src/utils.h create mode 100644 kernel/tools/virtio/.gitignore create mode 100644 kernel/tools/virtio/Makefile create mode 100644 kernel/tools/virtio/asm/barrier.h create mode 100644 kernel/tools/virtio/linux/bug.h create mode 100644 kernel/tools/virtio/linux/device.h create mode 100644 kernel/tools/virtio/linux/err.h create mode 100644 kernel/tools/virtio/linux/hrtimer.h create mode 100644 kernel/tools/virtio/linux/irqreturn.h create mode 100644 kernel/tools/virtio/linux/kernel.h create mode 100644 kernel/tools/virtio/linux/kmemleak.h create mode 100644 kernel/tools/virtio/linux/module.h create mode 100644 kernel/tools/virtio/linux/printk.h create mode 100644 kernel/tools/virtio/linux/ratelimit.h create mode 100644 kernel/tools/virtio/linux/scatterlist.h create mode 100644 kernel/tools/virtio/linux/slab.h create mode 100644 kernel/tools/virtio/linux/uaccess.h create mode 100644 kernel/tools/virtio/linux/uio.h create mode 100644 kernel/tools/virtio/linux/virtio.h create mode 100644 kernel/tools/virtio/linux/virtio_byteorder.h create mode 100644 kernel/tools/virtio/linux/virtio_config.h create mode 100644 kernel/tools/virtio/linux/virtio_ring.h create mode 100644 kernel/tools/virtio/linux/vringh.h create mode 100644 kernel/tools/virtio/uapi/linux/uio.h create mode 100644 kernel/tools/virtio/uapi/linux/virtio_config.h create mode 100644 kernel/tools/virtio/uapi/linux/virtio_ring.h create mode 100644 kernel/tools/virtio/uapi/linux/virtio_types.h create mode 100644 kernel/tools/virtio/vhost_test/Makefile create mode 100644 kernel/tools/virtio/vhost_test/vhost_test.c create mode 100644 kernel/tools/virtio/virtio-trace/Makefile create mode 100644 kernel/tools/virtio/virtio-trace/README create mode 100644 kernel/tools/virtio/virtio-trace/trace-agent-ctl.c create mode 100644 kernel/tools/virtio/virtio-trace/trace-agent-rw.c create mode 100644 kernel/tools/virtio/virtio-trace/trace-agent.c create mode 100644 kernel/tools/virtio/virtio-trace/trace-agent.h create mode 100644 kernel/tools/virtio/virtio_test.c create mode 100644 kernel/tools/virtio/vringh_test.c create mode 100644 kernel/tools/vm/.gitignore create mode 100644 kernel/tools/vm/Makefile create mode 100644 kernel/tools/vm/page-types.c create mode 100644 kernel/tools/vm/page_owner_sort.c create mode 100644 kernel/tools/vm/slabinfo.c (limited to 'kernel/tools') diff --git a/kernel/tools/Makefile b/kernel/tools/Makefile new file mode 100644 index 000000000..9a617adc6 --- /dev/null +++ b/kernel/tools/Makefile @@ -0,0 +1,119 @@ +include scripts/Makefile.include + +help: + @echo 'Possible targets:' + @echo '' + @echo ' acpi - ACPI tools' + @echo ' cgroup - cgroup tools' + @echo ' cpupower - a tool for all things x86 CPU power' + @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer' + @echo ' hv - tools used when in Hyper-V clients' + @echo ' lguest - a minimal 32-bit x86 hypervisor' + @echo ' perf - Linux performance measurement and analysis tool' + @echo ' selftests - various kernel selftests' + @echo ' turbostat - Intel CPU idle stats and freq reporting tool' + @echo ' usb - USB testing tools' + @echo ' virtio - vhost test module' + @echo ' net - misc networking tools' + @echo ' vm - misc vm tools' + @echo ' x86_energy_perf_policy - Intel energy policy tool' + @echo ' tmon - thermal monitoring and tuning tool' + @echo '' + @echo 'You can do:' + @echo ' $$ make -C tools/ _install' + @echo '' + @echo ' from the kernel command line to build and install one of' + @echo ' the tools above' + @echo '' + @echo ' $$ make tools/install' + @echo '' + @echo ' installs all tools.' + @echo '' + @echo 'Cleaning targets:' + @echo '' + @echo ' all of the above with the "_clean" string appended cleans' + @echo ' the respective build directory.' + @echo ' clean: a summary clean target to clean _all_ folders' + +acpi: FORCE + $(call descend,power/$@) + +cpupower: FORCE + $(call descend,power/$@) + +cgroup firewire hv guest usb virtio vm net: FORCE + $(call descend,$@) + +liblockdep: FORCE + $(call descend,lib/lockdep) + +libapikfs: FORCE + $(call descend,lib/api) + +perf: libapikfs FORCE + $(call descend,$@) + +selftests: FORCE + $(call descend,testing/$@) + +turbostat x86_energy_perf_policy: FORCE + $(call descend,power/x86/$@) + +tmon: FORCE + $(call descend,thermal/$@) + +acpi_install: + $(call descend,power/$(@:_install=),install) + +cpupower_install: + $(call descend,power/$(@:_install=),install) + +cgroup_install firewire_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install: + $(call descend,$(@:_install=),install) + +selftests_install: + $(call descend,testing/$(@:_clean=),install) + +turbostat_install x86_energy_perf_policy_install: + $(call descend,power/x86/$(@:_install=),install) + +tmon_install: + $(call descend,thermal/$(@:_install=),install) + +install: acpi_install cgroup_install cpupower_install hv_install firewire_install lguest_install \ + perf_install selftests_install turbostat_install usb_install \ + virtio_install vm_install net_install x86_energy_perf_policy_install \ + tmon + +acpi_clean: + $(call descend,power/acpi,clean) + +cpupower_clean: + $(call descend,power/cpupower,clean) + +cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean: + $(call descend,$(@:_clean=),clean) + +liblockdep_clean: + $(call descend,lib/lockdep,clean) + +libapikfs_clean: + $(call descend,lib/api,clean) + +perf_clean: libapikfs_clean + $(call descend,$(@:_clean=),clean) + +selftests_clean: + $(call descend,testing/$(@:_clean=),clean) + +turbostat_clean x86_energy_perf_policy_clean: + $(call descend,power/x86/$(@:_clean=),clean) + +tmon_clean: + $(call descend,thermal/tmon,clean) + +clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \ + perf_clean selftests_clean turbostat_clean usb_clean virtio_clean \ + vm_clean net_clean x86_energy_perf_policy_clean tmon_clean + +.PHONY: FORCE diff --git a/kernel/tools/build/Build.include b/kernel/tools/build/Build.include new file mode 100644 index 000000000..4c8daaccb --- /dev/null +++ b/kernel/tools/build/Build.include @@ -0,0 +1,81 @@ +### +# build: Generic definitions +# +# Lots of this code have been borrowed or heavily inspired from parts +# of kbuild code, which is not credited, but mostly developed by: +# +# Copyright (C) Sam Ravnborg , 2015 +# Copyright (C) Linus Torvalds , 2015 +# + +### +# Convenient variables +comma := , +squote := ' + +### +# Name of target with a '.' as filename prefix. foo/bar.o => foo/.bar.o +dot-target = $(dir $@).$(notdir $@) + +### +# filename of target with directory and extension stripped +basetarget = $(basename $(notdir $@)) + +### +# The temporary file to save gcc -MD generated dependencies must not +# contain a comma +depfile = $(subst $(comma),_,$(dot-target).d) + +### +# Check if both arguments has same arguments. Result is empty string if equal. +arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ + $(filter-out $(cmd_$@), $(cmd_$(1))) ) + +### +# Escape single quote for use in echo statements +escsq = $(subst $(squote),'\$(squote)',$1) + +# Echo command +# Short version is used, if $(quiet) equals `quiet_', otherwise full one. +echo-cmd = $(if $($(quiet)cmd_$(1)),\ + echo ' $(call escsq,$($(quiet)cmd_$(1)))';) + +### +# Replace >$< with >$$< to preserve $ when reloading the .cmd file +# (needed for make) +# Replace >#< with >\#< to avoid starting a comment in the .cmd file +# (needed for make) +# Replace >'< with >'\''< to be able to enclose the whole string in '...' +# (needed for the shell) +make-cmd = $(call escsq,$(subst \#,\\\#,$(subst $$,$$$$,$(cmd_$(1))))) + +### +# Find any prerequisites that is newer than target or that does not exist. +# PHONY targets skipped in both cases. +any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^) + +### +# if_changed_dep - execute command if any prerequisite is newer than +# target, or command line has changed and update +# dependencies in the cmd file +if_changed_dep = $(if $(strip $(any-prereq) $(arg-check)), \ + @set -e; \ + $(echo-cmd) $(cmd_$(1)); \ + cat $(depfile) > $(dot-target).cmd; \ + printf '%s\n' 'cmd_$@ := $(make-cmd)' >> $(dot-target).cmd) + +# if_changed - execute command if any prerequisite is newer than +# target, or command line has changed +if_changed = $(if $(strip $(any-prereq) $(arg-check)), \ + @set -e; \ + $(echo-cmd) $(cmd_$(1)); \ + printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd) + +### +# C flags to be used in rule definitions, includes: +# - depfile generation +# - global $(CFLAGS) +# - per target C flags +# - per object C flags +# - BUILD_STR macro to allow '-D"$(variable)"' constructs +c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CFLAGS) -D"BUILD_STR(s)=\#s" $(CFLAGS_$(basetarget).o) $(CFLAGS_$(obj)) diff --git a/kernel/tools/build/Documentation/Build.txt b/kernel/tools/build/Documentation/Build.txt new file mode 100644 index 000000000..00ad2d608 --- /dev/null +++ b/kernel/tools/build/Documentation/Build.txt @@ -0,0 +1,139 @@ +Build Framework +=============== + +The perf build framework was adopted from the kernel build system, hence the +idea and the way how objects are built is the same. + +Basically the user provides set of 'Build' files that list objects and +directories to nest for specific target to be build. + +Unlike the kernel we don't have a single build object 'obj-y' list that where +we setup source objects, but we support more. This allows one 'Build' file to +carry a sources list for multiple build objects. + +a) Build framework makefiles +---------------------------- + +The build framework consists of 2 Makefiles: + + Build.include + Makefile.build + +While the 'Build.include' file contains just some generic definitions, the +'Makefile.build' file is the makefile used from the outside. It's +interface/usage is following: + + $ make -f tools/build/Makefile srctree=$(KSRC) dir=$(DIR) obj=$(OBJECT) + +where: + + KSRC - is the path to kernel sources + DIR - is the path to the project to be built + OBJECT - is the name of the build object + +When succefully finished the $(DIR) directory contains the final object file +called $(OBJECT)-in.o: + + $ ls $(DIR)/$(OBJECT)-in.o + +which includes all compiled sources described in 'Build' makefiles. + +a) Build makefiles +------------------ + +The user supplies 'Build' makefiles that contains a objects list, and connects +the build to nested directories. + +Assume we have the following project structure: + + ex/a.c + /b.c + /c.c + /d.c + /arch/e.c + /arch/f.c + +Out of which you build the 'ex' binary ' and the 'libex.a' library: + + 'ex' - consists of 'a.o', 'b.o' and libex.a + 'libex.a' - consists of 'c.o', 'd.o', 'e.o' and 'f.o' + +The build framework does not create the 'ex' and 'libex.a' binaries for you, it +only prepares proper objects to be compiled and grouped together. + +To follow the above example, the user provides following 'Build' files: + + ex/Build: + ex-y += a.o + ex-y += b.o + + libex-y += c.o + libex-y += d.o + libex-y += arch/ + + ex/arch/Build: + libex-y += e.o + libex-y += f.o + +and runs: + + $ make -f tools/build/Makefile.build dir=. obj=ex + $ make -f tools/build/Makefile.build dir=. obj=libex + +which creates the following objects: + + ex/ex-in.o + ex/libex-in.o + +that contain request objects names in Build files. + +It's only a matter of 2 single commands to create the final binaries: + + $ ar rcs libex.a libex-in.o + $ gcc -o ex ex-in.o libex.a + +You can check the 'ex' example in 'tools/build/tests/ex' for more details. + +b) Rules +-------- + +The build framework provides standard compilation rules to handle .S and .c +compilation. + +It's possible to include special rule if needed (like we do for flex or bison +code generation). + +c) CFLAGS +--------- + +It's possible to alter the standard object C flags in the following way: + + CFLAGS_perf.o += '...' - alters CFLAGS for perf.o object + CFLAGS_gtk += '...' - alters CFLAGS for gtk build object + +This C flags changes has the scope of the Build makefile they are defined in. + + +d) Dependencies +--------------- + +For each built object file 'a.o' the '.a.cmd' is created and holds: + + - Command line used to built that object + (for each object) + + - Dependency rules generated by 'gcc -Wp,-MD,...' + (for compiled object) + +All existing '.cmd' files are included in the Build process to follow properly +the dependencies and trigger a rebuild when necessary. + + +e) Single rules +--------------- + +It's possible to build single object file by choice, like: + + $ make util/map.o # objects + $ make util/map.i # preprocessor + $ make util/map.s # assembly diff --git a/kernel/tools/build/Makefile.build b/kernel/tools/build/Makefile.build new file mode 100644 index 000000000..98cfc388e --- /dev/null +++ b/kernel/tools/build/Makefile.build @@ -0,0 +1,130 @@ +### +# Main build makefile. +# +# Lots of this code have been borrowed or heavily inspired from parts +# of kbuild code, which is not credited, but mostly developed by: +# +# Copyright (C) Sam Ravnborg , 2015 +# Copyright (C) Linus Torvalds , 2015 +# + +PHONY := __build +__build: + +ifeq ($(V),1) + quiet = + Q = +else + quiet=quiet_ + Q=@ +endif + +build-dir := $(srctree)/tools/build + +# Generic definitions +include $(build-dir)/Build.include + +# do not force detected configuration +-include .config-detected + +# Init all relevant variables used in build files so +# 1) they have correct type +# 2) they do not inherit any value from the environment +subdir-y := +obj-y := +subdir-y := +subdir-obj-y := + +# Build definitions +build-file := $(dir)/Build +include $(build-file) + +quiet_cmd_flex = FLEX $@ +quiet_cmd_bison = BISON $@ + +# Create directory unless it exists +quiet_cmd_mkdir = MKDIR $(dir $@) + cmd_mkdir = mkdir -p $(dir $@) + rule_mkdir = $(if $(wildcard $(dir $@)),,@$(call echo-cmd,mkdir) $(cmd_mkdir)) + +# Compile command +quiet_cmd_cc_o_c = CC $@ + cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< + +quiet_cmd_cc_i_c = CPP $@ + cmd_cc_i_c = $(CC) $(c_flags) -E -o $@ $< + +quiet_cmd_cc_s_c = AS $@ + cmd_cc_s_c = $(CC) $(c_flags) -S -o $@ $< + +# Link agregate command +# If there's nothing to link, create empty $@ object. +quiet_cmd_ld_multi = LD $@ + cmd_ld_multi = $(if $(strip $(obj-y)),\ + $(LD) -r -o $@ $(obj-y),rm -f $@; $(AR) rcs $@) + +# Build rules +$(OUTPUT)%.o: %.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) + +$(OUTPUT)%.o: %.S FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) + +$(OUTPUT)%.i: %.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_i_c) + +$(OUTPUT)%.i: %.S FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_i_c) + +$(OUTPUT)%.s: %.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_s_c) + +# Gather build data: +# obj-y - list of build objects +# subdir-y - list of directories to nest +# subdir-obj-y - list of directories objects 'dir/$(obj)-in.o' +obj-y := $($(obj)-y) +subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y))) +obj-y := $(patsubst %/, %/$(obj)-in.o, $(obj-y)) +subdir-obj-y := $(filter %/$(obj)-in.o, $(obj-y)) + +# '$(OUTPUT)/dir' prefix to all objects +objprefix := $(subst ./,,$(OUTPUT)$(dir)/) +obj-y := $(addprefix $(objprefix),$(obj-y)) +subdir-obj-y := $(addprefix $(objprefix),$(subdir-obj-y)) + +# Final '$(obj)-in.o' object +in-target := $(objprefix)$(obj)-in.o + +PHONY += $(subdir-y) + +$(subdir-y): + $(Q)$(MAKE) -f $(build-dir)/Makefile.build dir=$(dir)/$@ obj=$(obj) + +$(sort $(subdir-obj-y)): $(subdir-y) ; + +$(in-target): $(obj-y) FORCE + $(call rule_mkdir) + $(call if_changed,ld_multi) + +__build: $(in-target) + @: + +PHONY += FORCE +FORCE: + +# Include all cmd files to get all the dependency rules +# for all objects included +targets := $(wildcard $(sort $(obj-y) $(in-target) $(MAKECMDGOALS))) +cmd_files := $(wildcard $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) + +ifneq ($(cmd_files),) + include $(cmd_files) +endif + +.PHONY: $(PHONY) diff --git a/kernel/tools/build/Makefile.feature b/kernel/tools/build/Makefile.feature new file mode 100644 index 000000000..3a0b0ca2a --- /dev/null +++ b/kernel/tools/build/Makefile.feature @@ -0,0 +1,171 @@ +feature_dir := $(srctree)/tools/build/feature + +ifneq ($(OUTPUT),) + OUTPUT_FEATURES = $(OUTPUT)feature/ + $(shell mkdir -p $(OUTPUT_FEATURES)) +endif + +feature_check = $(eval $(feature_check_code)) +define feature_check_code + feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0) +endef + +feature_set = $(eval $(feature_set_code)) +define feature_set_code + feature-$(1) := 1 +endef + +# +# Build the feature check binaries in parallel, ignore errors, ignore return value and suppress output: +# + +# +# Note that this is not a complete list of all feature tests, just +# those that are typically built on a fully configured system. +# +# [ Feature tests not mentioned here have to be built explicitly in +# the rule that uses them - an example for that is the 'bionic' +# feature check. ] +# +FEATURE_TESTS = \ + backtrace \ + dwarf \ + fortify-source \ + sync-compare-and-swap \ + glibc \ + gtk2 \ + gtk2-infobar \ + libaudit \ + libbfd \ + libelf \ + libelf-getphdrnum \ + libelf-mmap \ + libnuma \ + libperl \ + libpython \ + libpython-version \ + libslang \ + libunwind \ + pthread-attr-setaffinity-np \ + stackprotector-all \ + timerfd \ + libdw-dwarf-unwind \ + zlib \ + lzma + +FEATURE_DISPLAY = \ + dwarf \ + glibc \ + gtk2 \ + libaudit \ + libbfd \ + libelf \ + libnuma \ + libperl \ + libpython \ + libslang \ + libunwind \ + libdw-dwarf-unwind \ + zlib \ + lzma + +# Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features. +# If in the future we need per-feature checks/flags for features not +# mentioned in this list we need to refactor this ;-). +set_test_all_flags = $(eval $(set_test_all_flags_code)) +define set_test_all_flags_code + FEATURE_CHECK_CFLAGS-all += $(FEATURE_CHECK_CFLAGS-$(1)) + FEATURE_CHECK_LDFLAGS-all += $(FEATURE_CHECK_LDFLAGS-$(1)) +endef + +$(foreach feat,$(FEATURE_TESTS),$(call set_test_all_flags,$(feat))) + +# +# Special fast-path for the 'all features are available' case: +# +$(call feature_check,all,$(MSG)) + +# +# Just in case the build freshly failed, make sure we print the +# feature matrix: +# +ifeq ($(feature-all), 1) + # + # test-all.c passed - just set all the core feature flags to 1: + # + $(foreach feat,$(FEATURE_TESTS),$(call feature_set,$(feat))) +else + $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) -i -j -C $(feature_dir) $(addsuffix .bin,$(FEATURE_TESTS)) >/dev/null 2>&1) + $(foreach feat,$(FEATURE_TESTS),$(call feature_check,$(feat))) +endif + +# +# Print the result of the feature test: +# +feature_print_status = $(eval $(feature_print_status_code)) $(info $(MSG)) + +define feature_print_status_code + ifeq ($(feature-$(1)), 1) + MSG = $(shell printf '...%30s: [ \033[32mon\033[m ]' $(1)) + else + MSG = $(shell printf '...%30s: [ \033[31mOFF\033[m ]' $(1)) + endif +endef + +feature_print_text = $(eval $(feature_print_text_code)) $(info $(MSG)) +define feature_print_text_code + MSG = $(shell printf '...%30s: %s' $(1) $(2)) +endef + +FEATURE_DUMP := $(foreach feat,$(FEATURE_DISPLAY),feature-$(feat)($(feature-$(feat)))) +FEATURE_DUMP_FILE := $(shell touch $(OUTPUT)FEATURE-DUMP; cat $(OUTPUT)FEATURE-DUMP) + +ifeq ($(dwarf-post-unwind),1) + FEATURE_DUMP += dwarf-post-unwind($(dwarf-post-unwind-text)) +endif + +# The $(feature_display) controls the default detection message +# output. It's set if: +# - detected features differes from stored features from +# last build (in FEATURE-DUMP file) +# - one of the $(FEATURE_DISPLAY) is not detected +# - VF is enabled + +ifneq ("$(FEATURE_DUMP)","$(FEATURE_DUMP_FILE)") + $(shell echo "$(FEATURE_DUMP)" > $(OUTPUT)FEATURE-DUMP) + feature_display := 1 +endif + +feature_display_check = $(eval $(feature_check_code)) +define feature_display_check_code + ifneq ($(feature-$(1)), 1) + feature_display := 1 + endif +endef + +$(foreach feat,$(FEATURE_DISPLAY),$(call feature_display_check,$(feat))) + +ifeq ($(VF),1) + feature_display := 1 + feature_verbose := 1 +endif + +ifeq ($(feature_display),1) + $(info ) + $(info Auto-detecting system features:) + $(foreach feat,$(FEATURE_DISPLAY),$(call feature_print_status,$(feat),)) + + ifeq ($(dwarf-post-unwind),1) + $(call feature_print_text,"DWARF post unwind library", $(dwarf-post-unwind-text)) + endif + + ifneq ($(feature_verbose),1) + $(info ) + endif +endif + +ifeq ($(feature_verbose),1) + TMP := $(filter-out $(FEATURE_DISPLAY),$(FEATURE_TESTS)) + $(foreach feat,$(TMP),$(call feature_print_status,$(feat),)) + $(info ) +endif diff --git a/kernel/tools/build/feature/.gitignore b/kernel/tools/build/feature/.gitignore new file mode 100644 index 000000000..09b335b98 --- /dev/null +++ b/kernel/tools/build/feature/.gitignore @@ -0,0 +1,3 @@ +*.d +*.bin +*.output diff --git a/kernel/tools/build/feature/Makefile b/kernel/tools/build/feature/Makefile new file mode 100644 index 000000000..463ed8f2a --- /dev/null +++ b/kernel/tools/build/feature/Makefile @@ -0,0 +1,164 @@ + +FILES= \ + test-all.bin \ + test-backtrace.bin \ + test-bionic.bin \ + test-dwarf.bin \ + test-fortify-source.bin \ + test-sync-compare-and-swap.bin \ + test-glibc.bin \ + test-gtk2.bin \ + test-gtk2-infobar.bin \ + test-hello.bin \ + test-libaudit.bin \ + test-libbfd.bin \ + test-liberty.bin \ + test-liberty-z.bin \ + test-cplus-demangle.bin \ + test-libelf.bin \ + test-libelf-getphdrnum.bin \ + test-libelf-mmap.bin \ + test-libnuma.bin \ + test-libperl.bin \ + test-libpython.bin \ + test-libpython-version.bin \ + test-libslang.bin \ + test-libunwind.bin \ + test-libunwind-debug-frame.bin \ + test-pthread-attr-setaffinity-np.bin \ + test-stackprotector-all.bin \ + test-timerfd.bin \ + test-libdw-dwarf-unwind.bin \ + test-libbabeltrace.bin \ + test-compile-32.bin \ + test-compile-x32.bin \ + test-zlib.bin \ + test-lzma.bin + +CC := $(CROSS_COMPILE)gcc -MD +PKG_CONFIG := $(CROSS_COMPILE)pkg-config + +all: $(FILES) + +__BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS) + BUILD = $(__BUILD) > $(OUTPUT)$(@:.bin=.make.output) 2>&1 + +############################### + +test-all.bin: + $(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz -llzma + +test-hello.bin: + $(BUILD) + +test-pthread-attr-setaffinity-np.bin: + $(BUILD) -D_GNU_SOURCE -lpthread + +test-stackprotector-all.bin: + $(BUILD) -fstack-protector-all + +test-fortify-source.bin: + $(BUILD) -O2 -D_FORTIFY_SOURCE=2 + +test-bionic.bin: + $(BUILD) + +test-libelf.bin: + $(BUILD) -lelf + +test-glibc.bin: + $(BUILD) + +test-dwarf.bin: + $(BUILD) -ldw + +test-libelf-mmap.bin: + $(BUILD) -lelf + +test-libelf-getphdrnum.bin: + $(BUILD) -lelf + +test-libnuma.bin: + $(BUILD) -lnuma + +test-libunwind.bin: + $(BUILD) -lelf + +test-libunwind-debug-frame.bin: + $(BUILD) -lelf + +test-libaudit.bin: + $(BUILD) -laudit + +test-libslang.bin: + $(BUILD) -I/usr/include/slang -lslang + +test-gtk2.bin: + $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) + +test-gtk2-infobar.bin: + $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) + +grep-libs = $(filter -l%,$(1)) +strip-libs = $(filter-out -l%,$(1)) + +PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null) +PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS)) +PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS)) +PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` +FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) + +test-libperl.bin: + $(BUILD) $(FLAGS_PERL_EMBED) + +test-libpython.bin: + $(BUILD) + +test-libpython-version.bin: + $(BUILD) + +test-libbfd.bin: + $(BUILD) -DPACKAGE='"perf"' -lbfd -lz -liberty -ldl + +test-liberty.bin: + $(CC) -Wall -Werror -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' -lbfd -ldl -liberty + +test-liberty-z.bin: + $(CC) -Wall -Werror -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' -lbfd -ldl -liberty -lz + +test-cplus-demangle.bin: + $(BUILD) -liberty + +test-backtrace.bin: + $(BUILD) + +test-timerfd.bin: + $(BUILD) + +test-libdw-dwarf-unwind.bin: + $(BUILD) # -ldw provided by $(FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind) + +test-libbabeltrace.bin: + $(BUILD) # -lbabeltrace provided by $(FEATURE_CHECK_LDFLAGS-libbabeltrace) + +test-sync-compare-and-swap.bin: + $(BUILD) + +test-compile-32.bin: + $(CC) -m32 -o $(OUTPUT)$@ test-compile.c + +test-compile-x32.bin: + $(CC) -mx32 -o $(OUTPUT)$@ test-compile.c + +test-zlib.bin: + $(BUILD) -lz + +test-lzma.bin: + $(BUILD) -llzma + +-include *.d + +############################### + +clean: + rm -f $(FILES) *.d $(FILES:.bin=.make.output) diff --git a/kernel/tools/build/feature/test-all.c b/kernel/tools/build/feature/test-all.c new file mode 100644 index 000000000..84689a678 --- /dev/null +++ b/kernel/tools/build/feature/test-all.c @@ -0,0 +1,148 @@ +/* + * test-all.c: Try to build all the main testcases at once. + * + * A well-configured system will have all the prereqs installed, so we can speed + * up auto-detection on such systems. + */ + +/* + * Quirk: Python and Perl headers cannot be in arbitrary places, so keep + * these 3 testcases at the top: + */ +#define main main_test_libpython +# include "test-libpython.c" +#undef main + +#define main main_test_libpython_version +# include "test-libpython-version.c" +#undef main + +#define main main_test_libperl +# include "test-libperl.c" +#undef main + +#define main main_test_hello +# include "test-hello.c" +#undef main + +#define main main_test_libelf +# include "test-libelf.c" +#undef main + +#define main main_test_libelf_mmap +# include "test-libelf-mmap.c" +#undef main + +#define main main_test_glibc +# include "test-glibc.c" +#undef main + +#define main main_test_dwarf +# include "test-dwarf.c" +#undef main + +#define main main_test_libelf_getphdrnum +# include "test-libelf-getphdrnum.c" +#undef main + +#define main main_test_libunwind +# include "test-libunwind.c" +#undef main + +#define main main_test_libaudit +# include "test-libaudit.c" +#undef main + +#define main main_test_libslang +# include "test-libslang.c" +#undef main + +#define main main_test_gtk2 +# include "test-gtk2.c" +#undef main + +#define main main_test_gtk2_infobar +# include "test-gtk2-infobar.c" +#undef main + +#define main main_test_libbfd +# include "test-libbfd.c" +#undef main + +#define main main_test_backtrace +# include "test-backtrace.c" +#undef main + +#define main main_test_libnuma +# include "test-libnuma.c" +#undef main + +#define main main_test_timerfd +# include "test-timerfd.c" +#undef main + +#define main main_test_stackprotector_all +# include "test-stackprotector-all.c" +#undef main + +#define main main_test_libdw_dwarf_unwind +# include "test-libdw-dwarf-unwind.c" +#undef main + +#define main main_test_sync_compare_and_swap +# include "test-sync-compare-and-swap.c" +#undef main + +#define main main_test_zlib +# include "test-zlib.c" +#undef main + +#define main main_test_pthread_attr_setaffinity_np +# include "test-pthread-attr-setaffinity-np.c" +#undef main + +# if 0 +/* + * Disable libbabeltrace check for test-all, because the requested + * library version is not released yet in most distributions. Will + * reenable later. + */ + +#define main main_test_libbabeltrace +# include "test-libbabeltrace.c" +#undef main +#endif + +#define main main_test_lzma +# include "test-lzma.c" +#undef main + +int main(int argc, char *argv[]) +{ + main_test_libpython(); + main_test_libpython_version(); + main_test_libperl(); + main_test_hello(); + main_test_libelf(); + main_test_libelf_mmap(); + main_test_glibc(); + main_test_dwarf(); + main_test_libelf_getphdrnum(); + main_test_libunwind(); + main_test_libaudit(); + main_test_libslang(); + main_test_gtk2(argc, argv); + main_test_gtk2_infobar(argc, argv); + main_test_libbfd(); + main_test_backtrace(); + main_test_libnuma(); + main_test_timerfd(); + main_test_stackprotector_all(); + main_test_libdw_dwarf_unwind(); + main_test_sync_compare_and_swap(argc, argv); + main_test_zlib(); + main_test_pthread_attr_setaffinity_np(); + main_test_lzma(); + + return 0; +} diff --git a/kernel/tools/build/feature/test-backtrace.c b/kernel/tools/build/feature/test-backtrace.c new file mode 100644 index 000000000..7124aa1dc --- /dev/null +++ b/kernel/tools/build/feature/test-backtrace.c @@ -0,0 +1,13 @@ +#include +#include + +int main(void) +{ + void *backtrace_fns[10]; + size_t entries; + + entries = backtrace(backtrace_fns, 10); + backtrace_symbols_fd(backtrace_fns, entries, 1); + + return 0; +} diff --git a/kernel/tools/build/feature/test-bionic.c b/kernel/tools/build/feature/test-bionic.c new file mode 100644 index 000000000..eac24e951 --- /dev/null +++ b/kernel/tools/build/feature/test-bionic.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return __ANDROID_API__; +} diff --git a/kernel/tools/build/feature/test-compile.c b/kernel/tools/build/feature/test-compile.c new file mode 100644 index 000000000..31dbf45bf --- /dev/null +++ b/kernel/tools/build/feature/test-compile.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/kernel/tools/build/feature/test-cplus-demangle.c b/kernel/tools/build/feature/test-cplus-demangle.c new file mode 100644 index 000000000..610c686e0 --- /dev/null +++ b/kernel/tools/build/feature/test-cplus-demangle.c @@ -0,0 +1,14 @@ +extern int printf(const char *format, ...); +extern char *cplus_demangle(const char *, int); + +int main(void) +{ + char symbol[4096] = "FieldName__9ClassNameFd"; + char *tmp; + + tmp = cplus_demangle(symbol, 0); + + printf("demangled symbol: {%s}\n", tmp); + + return 0; +} diff --git a/kernel/tools/build/feature/test-dwarf.c b/kernel/tools/build/feature/test-dwarf.c new file mode 100644 index 000000000..3fc1801ce --- /dev/null +++ b/kernel/tools/build/feature/test-dwarf.c @@ -0,0 +1,10 @@ +#include +#include +#include + +int main(void) +{ + Dwarf *dbg = dwarf_begin(0, DWARF_C_READ); + + return (long)dbg; +} diff --git a/kernel/tools/build/feature/test-fortify-source.c b/kernel/tools/build/feature/test-fortify-source.c new file mode 100644 index 000000000..c9f398d87 --- /dev/null +++ b/kernel/tools/build/feature/test-fortify-source.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return puts("hi"); +} diff --git a/kernel/tools/build/feature/test-glibc.c b/kernel/tools/build/feature/test-glibc.c new file mode 100644 index 000000000..b0820345c --- /dev/null +++ b/kernel/tools/build/feature/test-glibc.c @@ -0,0 +1,8 @@ +#include + +int main(void) +{ + const char *version = gnu_get_libc_version(); + + return (long)version; +} diff --git a/kernel/tools/build/feature/test-gtk2-infobar.c b/kernel/tools/build/feature/test-gtk2-infobar.c new file mode 100644 index 000000000..397b4646d --- /dev/null +++ b/kernel/tools/build/feature/test-gtk2-infobar.c @@ -0,0 +1,11 @@ +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#include +#pragma GCC diagnostic error "-Wstrict-prototypes" + +int main(int argc, char *argv[]) +{ + gtk_init(&argc, &argv); + gtk_info_bar_new(); + + return 0; +} diff --git a/kernel/tools/build/feature/test-gtk2.c b/kernel/tools/build/feature/test-gtk2.c new file mode 100644 index 000000000..6bd80e509 --- /dev/null +++ b/kernel/tools/build/feature/test-gtk2.c @@ -0,0 +1,10 @@ +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#include +#pragma GCC diagnostic error "-Wstrict-prototypes" + +int main(int argc, char *argv[]) +{ + gtk_init(&argc, &argv); + + return 0; +} diff --git a/kernel/tools/build/feature/test-hello.c b/kernel/tools/build/feature/test-hello.c new file mode 100644 index 000000000..c9f398d87 --- /dev/null +++ b/kernel/tools/build/feature/test-hello.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return puts("hi"); +} diff --git a/kernel/tools/build/feature/test-libaudit.c b/kernel/tools/build/feature/test-libaudit.c new file mode 100644 index 000000000..afc019f08 --- /dev/null +++ b/kernel/tools/build/feature/test-libaudit.c @@ -0,0 +1,10 @@ +#include + +extern int printf(const char *format, ...); + +int main(void) +{ + printf("error message: %s\n", audit_errno_to_name(0)); + + return audit_open(); +} diff --git a/kernel/tools/build/feature/test-libbabeltrace.c b/kernel/tools/build/feature/test-libbabeltrace.c new file mode 100644 index 000000000..9cf802a04 --- /dev/null +++ b/kernel/tools/build/feature/test-libbabeltrace.c @@ -0,0 +1,9 @@ + +#include +#include + +int main(void) +{ + bt_ctf_stream_class_get_packet_context_type((void *) 0); + return 0; +} diff --git a/kernel/tools/build/feature/test-libbfd.c b/kernel/tools/build/feature/test-libbfd.c new file mode 100644 index 000000000..24059907e --- /dev/null +++ b/kernel/tools/build/feature/test-libbfd.c @@ -0,0 +1,15 @@ +#include + +extern int printf(const char *format, ...); + +int main(void) +{ + char symbol[4096] = "FieldName__9ClassNameFd"; + char *tmp; + + tmp = bfd_demangle(0, symbol, 0); + + printf("demangled symbol: {%s}\n", tmp); + + return 0; +} diff --git a/kernel/tools/build/feature/test-libdw-dwarf-unwind.c b/kernel/tools/build/feature/test-libdw-dwarf-unwind.c new file mode 100644 index 000000000..f676a3ff4 --- /dev/null +++ b/kernel/tools/build/feature/test-libdw-dwarf-unwind.c @@ -0,0 +1,13 @@ + +#include + +int main(void) +{ + /* + * This function is guarded via: __nonnull_attribute__ (1, 2). + * Passing '1' as arguments value. This code is never executed, + * only compiled. + */ + dwfl_thread_getframes((void *) 1, (void *) 1, NULL); + return 0; +} diff --git a/kernel/tools/build/feature/test-libelf-getphdrnum.c b/kernel/tools/build/feature/test-libelf-getphdrnum.c new file mode 100644 index 000000000..d71045930 --- /dev/null +++ b/kernel/tools/build/feature/test-libelf-getphdrnum.c @@ -0,0 +1,8 @@ +#include + +int main(void) +{ + size_t dst; + + return elf_getphdrnum(0, &dst); +} diff --git a/kernel/tools/build/feature/test-libelf-mmap.c b/kernel/tools/build/feature/test-libelf-mmap.c new file mode 100644 index 000000000..564427d7e --- /dev/null +++ b/kernel/tools/build/feature/test-libelf-mmap.c @@ -0,0 +1,8 @@ +#include + +int main(void) +{ + Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0); + + return (long)elf; +} diff --git a/kernel/tools/build/feature/test-libelf.c b/kernel/tools/build/feature/test-libelf.c new file mode 100644 index 000000000..08db322d8 --- /dev/null +++ b/kernel/tools/build/feature/test-libelf.c @@ -0,0 +1,8 @@ +#include + +int main(void) +{ + Elf *elf = elf_begin(0, ELF_C_READ, 0); + + return (long)elf; +} diff --git a/kernel/tools/build/feature/test-libnuma.c b/kernel/tools/build/feature/test-libnuma.c new file mode 100644 index 000000000..4763d9cd5 --- /dev/null +++ b/kernel/tools/build/feature/test-libnuma.c @@ -0,0 +1,9 @@ +#include +#include + +int main(void) +{ + numa_available(); + + return 0; +} diff --git a/kernel/tools/build/feature/test-libperl.c b/kernel/tools/build/feature/test-libperl.c new file mode 100644 index 000000000..8871f6a0f --- /dev/null +++ b/kernel/tools/build/feature/test-libperl.c @@ -0,0 +1,9 @@ +#include +#include + +int main(void) +{ + perl_alloc(); + + return 0; +} diff --git a/kernel/tools/build/feature/test-libpython-version.c b/kernel/tools/build/feature/test-libpython-version.c new file mode 100644 index 000000000..facea122d --- /dev/null +++ b/kernel/tools/build/feature/test-libpython-version.c @@ -0,0 +1,10 @@ +#include + +#if PY_VERSION_HEX >= 0x03000000 + #error +#endif + +int main(void) +{ + return 0; +} diff --git a/kernel/tools/build/feature/test-libpython.c b/kernel/tools/build/feature/test-libpython.c new file mode 100644 index 000000000..b24b28ad6 --- /dev/null +++ b/kernel/tools/build/feature/test-libpython.c @@ -0,0 +1,8 @@ +#include + +int main(void) +{ + Py_Initialize(); + + return 0; +} diff --git a/kernel/tools/build/feature/test-libslang.c b/kernel/tools/build/feature/test-libslang.c new file mode 100644 index 000000000..22ff22ed9 --- /dev/null +++ b/kernel/tools/build/feature/test-libslang.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return SLsmg_init_smg(); +} diff --git a/kernel/tools/build/feature/test-libunwind-debug-frame.c b/kernel/tools/build/feature/test-libunwind-debug-frame.c new file mode 100644 index 000000000..0ef8087a1 --- /dev/null +++ b/kernel/tools/build/feature/test-libunwind-debug-frame.c @@ -0,0 +1,16 @@ +#include +#include + +extern int +UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, + unw_word_t ip, unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); + +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) + +int main(void) +{ + dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0); + return 0; +} diff --git a/kernel/tools/build/feature/test-libunwind.c b/kernel/tools/build/feature/test-libunwind.c new file mode 100644 index 000000000..43b9369bc --- /dev/null +++ b/kernel/tools/build/feature/test-libunwind.c @@ -0,0 +1,27 @@ +#include +#include + +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); + + +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) + +static unw_accessors_t accessors; + +int main(void) +{ + unw_addr_space_t addr_space; + + addr_space = unw_create_addr_space(&accessors, 0); + if (addr_space) + return 0; + + unw_init_remote(NULL, addr_space, NULL); + dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); + + return 0; +} diff --git a/kernel/tools/build/feature/test-lzma.c b/kernel/tools/build/feature/test-lzma.c new file mode 100644 index 000000000..95adc8ced --- /dev/null +++ b/kernel/tools/build/feature/test-lzma.c @@ -0,0 +1,10 @@ +#include + +int main(void) +{ + lzma_stream strm = LZMA_STREAM_INIT; + int ret; + + ret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED); + return ret ? -1 : 0; +} diff --git a/kernel/tools/build/feature/test-pthread-attr-setaffinity-np.c b/kernel/tools/build/feature/test-pthread-attr-setaffinity-np.c new file mode 100644 index 000000000..fdada5e8d --- /dev/null +++ b/kernel/tools/build/feature/test-pthread-attr-setaffinity-np.c @@ -0,0 +1,17 @@ +#include +#include +#include + +int main(void) +{ + int ret = 0; + pthread_attr_t thread_attr; + cpu_set_t cs; + + pthread_attr_init(&thread_attr); + CPU_ZERO(&cs); + + ret = pthread_attr_setaffinity_np(&thread_attr, sizeof(cs), &cs); + + return ret; +} diff --git a/kernel/tools/build/feature/test-stackprotector-all.c b/kernel/tools/build/feature/test-stackprotector-all.c new file mode 100644 index 000000000..c9f398d87 --- /dev/null +++ b/kernel/tools/build/feature/test-stackprotector-all.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return puts("hi"); +} diff --git a/kernel/tools/build/feature/test-sync-compare-and-swap.c b/kernel/tools/build/feature/test-sync-compare-and-swap.c new file mode 100644 index 000000000..c34d4ca4a --- /dev/null +++ b/kernel/tools/build/feature/test-sync-compare-and-swap.c @@ -0,0 +1,14 @@ +#include + +volatile uint64_t x; + +int main(int argc, char *argv[]) +{ + uint64_t old, new = argc; + + argv = argv; + do { + old = __sync_val_compare_and_swap(&x, 0, 0); + } while (!__sync_bool_compare_and_swap(&x, old, new)); + return old == new; +} diff --git a/kernel/tools/build/feature/test-timerfd.c b/kernel/tools/build/feature/test-timerfd.c new file mode 100644 index 000000000..8c5c083b4 --- /dev/null +++ b/kernel/tools/build/feature/test-timerfd.c @@ -0,0 +1,18 @@ +/* + * test for timerfd functions used by perf-kvm-stat-live + */ +#include + +int main(void) +{ + struct itimerspec new_value; + + int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + if (fd < 0) + return 1; + + if (timerfd_settime(fd, 0, &new_value, NULL) != 0) + return 1; + + return 0; +} diff --git a/kernel/tools/build/feature/test-zlib.c b/kernel/tools/build/feature/test-zlib.c new file mode 100644 index 000000000..e111fff62 --- /dev/null +++ b/kernel/tools/build/feature/test-zlib.c @@ -0,0 +1,9 @@ +#include + +int main(void) +{ + z_stream zs; + + inflateInit(&zs); + return 0; +} diff --git a/kernel/tools/build/tests/ex/Build b/kernel/tools/build/tests/ex/Build new file mode 100644 index 000000000..0e6c3e676 --- /dev/null +++ b/kernel/tools/build/tests/ex/Build @@ -0,0 +1,8 @@ +ex-y += ex.o +ex-y += a.o +ex-y += b.o +ex-y += empty/ + +libex-y += c.o +libex-y += d.o +libex-y += arch/ diff --git a/kernel/tools/build/tests/ex/Makefile b/kernel/tools/build/tests/ex/Makefile new file mode 100644 index 000000000..52d247607 --- /dev/null +++ b/kernel/tools/build/tests/ex/Makefile @@ -0,0 +1,23 @@ +export srctree := ../../../.. +export CC := gcc +export LD := ld +export AR := ar + +build := -f $(srctree)/tools/build/Makefile.build dir=. obj +ex: ex-in.o libex-in.o + gcc -o $@ $^ + +ex.%: FORCE + make -f $(srctree)/tools/build/Makefile.build dir=. $@ + +ex-in.o: FORCE + make $(build)=ex + +libex-in.o: FORCE + make $(build)=libex + +clean: + find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete + rm -f ex ex.i ex.s + +.PHONY: FORCE diff --git a/kernel/tools/build/tests/ex/a.c b/kernel/tools/build/tests/ex/a.c new file mode 100644 index 000000000..851762798 --- /dev/null +++ b/kernel/tools/build/tests/ex/a.c @@ -0,0 +1,5 @@ + +int a(void) +{ + return 0; +} diff --git a/kernel/tools/build/tests/ex/arch/Build b/kernel/tools/build/tests/ex/arch/Build new file mode 100644 index 000000000..55506189e --- /dev/null +++ b/kernel/tools/build/tests/ex/arch/Build @@ -0,0 +1,2 @@ +libex-y += e.o +libex-y += f.o diff --git a/kernel/tools/build/tests/ex/arch/e.c b/kernel/tools/build/tests/ex/arch/e.c new file mode 100644 index 000000000..beaa4a1d7 --- /dev/null +++ b/kernel/tools/build/tests/ex/arch/e.c @@ -0,0 +1,5 @@ + +int e(void) +{ + return 0; +} diff --git a/kernel/tools/build/tests/ex/arch/f.c b/kernel/tools/build/tests/ex/arch/f.c new file mode 100644 index 000000000..7c3e9e9da --- /dev/null +++ b/kernel/tools/build/tests/ex/arch/f.c @@ -0,0 +1,5 @@ + +int f(void) +{ + return 0; +} diff --git a/kernel/tools/build/tests/ex/b.c b/kernel/tools/build/tests/ex/b.c new file mode 100644 index 000000000..c24ff9ca9 --- /dev/null +++ b/kernel/tools/build/tests/ex/b.c @@ -0,0 +1,5 @@ + +int b(void) +{ + return 0; +} diff --git a/kernel/tools/build/tests/ex/c.c b/kernel/tools/build/tests/ex/c.c new file mode 100644 index 000000000..e216d0217 --- /dev/null +++ b/kernel/tools/build/tests/ex/c.c @@ -0,0 +1,5 @@ + +int c(void) +{ + return 0; +} diff --git a/kernel/tools/build/tests/ex/d.c b/kernel/tools/build/tests/ex/d.c new file mode 100644 index 000000000..80dc0f061 --- /dev/null +++ b/kernel/tools/build/tests/ex/d.c @@ -0,0 +1,5 @@ + +int d(void) +{ + return 0; +} diff --git a/kernel/tools/build/tests/ex/empty/Build b/kernel/tools/build/tests/ex/empty/Build new file mode 100644 index 000000000..e69de29bb diff --git a/kernel/tools/build/tests/ex/ex.c b/kernel/tools/build/tests/ex/ex.c new file mode 100644 index 000000000..dc42eb2e1 --- /dev/null +++ b/kernel/tools/build/tests/ex/ex.c @@ -0,0 +1,19 @@ + +int a(void); +int b(void); +int c(void); +int d(void); +int e(void); +int f(void); + +int main(void) +{ + a(); + b(); + c(); + d(); + e(); + f(); + + return 0; +} diff --git a/kernel/tools/build/tests/run.sh b/kernel/tools/build/tests/run.sh new file mode 100755 index 000000000..5494f8ea7 --- /dev/null +++ b/kernel/tools/build/tests/run.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +function test_ex { + make -C ex V=1 clean > ex.out 2>&1 + make -C ex V=1 >> ex.out 2>&1 + + if [ ! -x ./ex/ex ]; then + echo FAILED + exit -1 + fi + + make -C ex V=1 clean > /dev/null 2>&1 + rm -f ex.out +} + +function test_ex_suffix { + make -C ex V=1 clean > ex.out 2>&1 + + # use -rR to disable make's builtin rules + make -rR -C ex V=1 ex.o >> ex.out 2>&1 + make -rR -C ex V=1 ex.i >> ex.out 2>&1 + make -rR -C ex V=1 ex.s >> ex.out 2>&1 + + if [ -x ./ex/ex ]; then + echo FAILED + exit -1 + fi + + if [ ! -f ./ex/ex.o -o ! -f ./ex/ex.i -o ! -f ./ex/ex.s ]; then + echo FAILED + exit -1 + fi + + make -C ex V=1 clean > /dev/null 2>&1 + rm -f ex.out +} +echo -n Testing.. + +test_ex +test_ex_suffix + +echo OK diff --git a/kernel/tools/cgroup/.gitignore b/kernel/tools/cgroup/.gitignore new file mode 100644 index 000000000..633cd9b87 --- /dev/null +++ b/kernel/tools/cgroup/.gitignore @@ -0,0 +1 @@ +cgroup_event_listener diff --git a/kernel/tools/cgroup/Makefile b/kernel/tools/cgroup/Makefile new file mode 100644 index 000000000..b4286196b --- /dev/null +++ b/kernel/tools/cgroup/Makefile @@ -0,0 +1,11 @@ +# Makefile for cgroup tools + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall -Wextra + +all: cgroup_event_listener +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +clean: + $(RM) cgroup_event_listener diff --git a/kernel/tools/cgroup/cgroup_event_listener.c b/kernel/tools/cgroup/cgroup_event_listener.c new file mode 100644 index 000000000..4eb550720 --- /dev/null +++ b/kernel/tools/cgroup/cgroup_event_listener.c @@ -0,0 +1,82 @@ +/* + * cgroup_event_listener.c - Simple listener of cgroup events + * + * Copyright (C) Kirill A. Shutemov + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define USAGE_STR "Usage: cgroup_event_listener " + +int main(int argc, char **argv) +{ + int efd = -1; + int cfd = -1; + int event_control = -1; + char event_control_path[PATH_MAX]; + char line[LINE_MAX]; + int ret; + + if (argc != 3) + errx(1, "%s", USAGE_STR); + + cfd = open(argv[1], O_RDONLY); + if (cfd == -1) + err(1, "Cannot open %s", argv[1]); + + ret = snprintf(event_control_path, PATH_MAX, "%s/cgroup.event_control", + dirname(argv[1])); + if (ret >= PATH_MAX) + errx(1, "Path to cgroup.event_control is too long"); + + event_control = open(event_control_path, O_WRONLY); + if (event_control == -1) + err(1, "Cannot open %s", event_control_path); + + efd = eventfd(0, 0); + if (efd == -1) + err(1, "eventfd() failed"); + + ret = snprintf(line, LINE_MAX, "%d %d %s", efd, cfd, argv[2]); + if (ret >= LINE_MAX) + errx(1, "Arguments string is too long"); + + ret = write(event_control, line, strlen(line) + 1); + if (ret == -1) + err(1, "Cannot write to cgroup.event_control"); + + while (1) { + uint64_t result; + + ret = read(efd, &result, sizeof(result)); + if (ret == -1) { + if (errno == EINTR) + continue; + err(1, "Cannot read from eventfd"); + } + assert(ret == sizeof(result)); + + ret = access(event_control_path, W_OK); + if ((ret == -1) && (errno == ENOENT)) { + puts("The cgroup seems to have removed."); + break; + } + + if (ret == -1) + err(1, "cgroup.event_control is not accessible any more"); + + printf("%s %s: crossed\n", argv[1], argv[2]); + } + + return 0; +} diff --git a/kernel/tools/firewire/Makefile b/kernel/tools/firewire/Makefile new file mode 100644 index 000000000..81767adaa --- /dev/null +++ b/kernel/tools/firewire/Makefile @@ -0,0 +1,19 @@ +prefix = /usr +nosy-dump-version = 0.4 + +CC = gcc + +all : nosy-dump + +nosy-dump : CFLAGS = -Wall -O2 -g +nosy-dump : CPPFLAGS = -DVERSION=\"$(nosy-dump-version)\" -I../../drivers/firewire +nosy-dump : LDFLAGS = -g +nosy-dump : LDLIBS = -lpopt + +nosy-dump : nosy-dump.o decode-fcp.o + +clean : + rm -rf *.o nosy-dump + +install : + install nosy-dump $(prefix)/bin/nosy-dump diff --git a/kernel/tools/firewire/decode-fcp.c b/kernel/tools/firewire/decode-fcp.c new file mode 100644 index 000000000..e41223b6a --- /dev/null +++ b/kernel/tools/firewire/decode-fcp.c @@ -0,0 +1,213 @@ +#include +#include +#include + +#include "list.h" +#include "nosy-dump.h" + +#define CSR_FCP_COMMAND 0xfffff0000b00ull +#define CSR_FCP_RESPONSE 0xfffff0000d00ull + +static const char * const ctype_names[] = { + [0x0] = "control", [0x8] = "not implemented", + [0x1] = "status", [0x9] = "accepted", + [0x2] = "specific inquiry", [0xa] = "rejected", + [0x3] = "notify", [0xb] = "in transition", + [0x4] = "general inquiry", [0xc] = "stable", + [0x5] = "(reserved 0x05)", [0xd] = "changed", + [0x6] = "(reserved 0x06)", [0xe] = "(reserved 0x0e)", + [0x7] = "(reserved 0x07)", [0xf] = "interim", +}; + +static const char * const subunit_type_names[] = { + [0x00] = "monitor", [0x10] = "(reserved 0x10)", + [0x01] = "audio", [0x11] = "(reserved 0x11)", + [0x02] = "printer", [0x12] = "(reserved 0x12)", + [0x03] = "disc", [0x13] = "(reserved 0x13)", + [0x04] = "tape recorder/player",[0x14] = "(reserved 0x14)", + [0x05] = "tuner", [0x15] = "(reserved 0x15)", + [0x06] = "ca", [0x16] = "(reserved 0x16)", + [0x07] = "camera", [0x17] = "(reserved 0x17)", + [0x08] = "(reserved 0x08)", [0x18] = "(reserved 0x18)", + [0x09] = "panel", [0x19] = "(reserved 0x19)", + [0x0a] = "bulletin board", [0x1a] = "(reserved 0x1a)", + [0x0b] = "camera storage", [0x1b] = "(reserved 0x1b)", + [0x0c] = "(reserved 0x0c)", [0x1c] = "vendor unique", + [0x0d] = "(reserved 0x0d)", [0x1d] = "all subunit types", + [0x0e] = "(reserved 0x0e)", [0x1e] = "subunit_type extended to next byte", + [0x0f] = "(reserved 0x0f)", [0x1f] = "unit", +}; + +struct avc_enum { + int value; + const char *name; +}; + +struct avc_field { + const char *name; /* Short name for field. */ + int offset; /* Location of field, specified in bits; */ + /* negative means from end of packet. */ + int width; /* Width of field, 0 means use data_length. */ + struct avc_enum *names; +}; + +struct avc_opcode_info { + const char *name; + struct avc_field fields[8]; +}; + +struct avc_enum power_field_names[] = { + { 0x70, "on" }, + { 0x60, "off" }, + { } +}; + +static const struct avc_opcode_info opcode_info[256] = { + + /* TA Document 1999026 */ + /* AV/C Digital Interface Command Set General Specification 4.0 */ + [0xb2] = { "power", { + { "state", 0, 8, power_field_names } + } + }, + [0x30] = { "unit info", { + { "foo", 0, 8 }, + { "unit_type", 8, 5 }, + { "unit", 13, 3 }, + { "company id", 16, 24 }, + } + }, + [0x31] = { "subunit info" }, + [0x01] = { "reserve" }, + [0xb0] = { "version" }, + [0x00] = { "vendor dependent" }, + [0x02] = { "plug info" }, + [0x12] = { "channel usage" }, + [0x24] = { "connect" }, + [0x20] = { "connect av" }, + [0x22] = { "connections" }, + [0x11] = { "digital input" }, + [0x10] = { "digital output" }, + [0x25] = { "disconnect" }, + [0x21] = { "disconnect av" }, + [0x19] = { "input plug signal format" }, + [0x18] = { "output plug signal format" }, + [0x1f] = { "general bus setup" }, + + /* TA Document 1999025 */ + /* AV/C Descriptor Mechanism Specification Version 1.0 */ + [0x0c] = { "create descriptor" }, + [0x08] = { "open descriptor" }, + [0x09] = { "read descriptor" }, + [0x0a] = { "write descriptor" }, + [0x05] = { "open info block" }, + [0x06] = { "read info block" }, + [0x07] = { "write info block" }, + [0x0b] = { "search descriptor" }, + [0x0d] = { "object number select" }, + + /* TA Document 1999015 */ + /* AV/C Command Set for Rate Control of Isochronous Data Flow 1.0 */ + [0xb3] = { "rate", { + { "subfunction", 0, 8 }, + { "result", 8, 8 }, + { "plug_type", 16, 8 }, + { "plug_id", 16, 8 }, + } + }, + + /* TA Document 1999008 */ + /* AV/C Audio Subunit Specification 1.0 */ + [0xb8] = { "function block" }, + + /* TA Document 2001001 */ + /* AV/C Panel Subunit Specification 1.1 */ + [0x7d] = { "gui update" }, + [0x7e] = { "push gui data" }, + [0x7f] = { "user action" }, + [0x7c] = { "pass through" }, + + /* */ + [0x26] = { "asynchronous connection" }, +}; + +struct avc_frame { + uint32_t operand0:8; + uint32_t opcode:8; + uint32_t subunit_id:3; + uint32_t subunit_type:5; + uint32_t ctype:4; + uint32_t cts:4; +}; + +static void +decode_avc(struct link_transaction *t) +{ + struct avc_frame *frame = + (struct avc_frame *) t->request->packet.write_block.data; + const struct avc_opcode_info *info; + const char *name; + char buffer[32]; + int i; + + info = &opcode_info[frame->opcode]; + if (info->name == NULL) { + snprintf(buffer, sizeof(buffer), + "(unknown opcode 0x%02x)", frame->opcode); + name = buffer; + } else { + name = info->name; + } + + printf("av/c %s, subunit_type=%s, subunit_id=%d, opcode=%s", + ctype_names[frame->ctype], subunit_type_names[frame->subunit_type], + frame->subunit_id, name); + + for (i = 0; info->fields[i].name != NULL; i++) + printf(", %s", info->fields[i].name); + + printf("\n"); +} + +int +decode_fcp(struct link_transaction *t) +{ + struct avc_frame *frame = + (struct avc_frame *) t->request->packet.write_block.data; + unsigned long long offset = + ((unsigned long long) t->request->packet.common.offset_high << 32) | + t->request->packet.common.offset_low; + + if (t->request->packet.common.tcode != TCODE_WRITE_BLOCK_REQUEST) + return 0; + + if (offset == CSR_FCP_COMMAND || offset == CSR_FCP_RESPONSE) { + switch (frame->cts) { + case 0x00: + decode_avc(t); + break; + case 0x01: + printf("cal fcp frame (cts=0x01)\n"); + break; + case 0x02: + printf("ehs fcp frame (cts=0x02)\n"); + break; + case 0x03: + printf("havi fcp frame (cts=0x03)\n"); + break; + case 0x0e: + printf("vendor specific fcp frame (cts=0x0e)\n"); + break; + case 0x0f: + printf("extended cts\n"); + break; + default: + printf("reserved fcp frame (ctx=0x%02x)\n", frame->cts); + break; + } + return 1; + } + + return 0; +} + diff --git a/kernel/tools/firewire/list.h b/kernel/tools/firewire/list.h new file mode 100644 index 000000000..41f4bdadf --- /dev/null +++ b/kernel/tools/firewire/list.h @@ -0,0 +1,62 @@ +struct list { + struct list *next, *prev; +}; + +static inline void +list_init(struct list *list) +{ + list->next = list; + list->prev = list; +} + +static inline int +list_empty(struct list *list) +{ + return list->next == list; +} + +static inline void +list_insert(struct list *link, struct list *new_link) +{ + new_link->prev = link->prev; + new_link->next = link; + new_link->prev->next = new_link; + new_link->next->prev = new_link; +} + +static inline void +list_append(struct list *list, struct list *new_link) +{ + list_insert((struct list *)list, new_link); +} + +static inline void +list_prepend(struct list *list, struct list *new_link) +{ + list_insert(list->next, new_link); +} + +static inline void +list_remove(struct list *link) +{ + link->prev->next = link->next; + link->next->prev = link->prev; +} + +#define list_entry(link, type, member) \ + ((type *)((char *)(link)-(unsigned long)(&((type *)0)->member))) + +#define list_head(list, type, member) \ + list_entry((list)->next, type, member) + +#define list_tail(list, type, member) \ + list_entry((list)->prev, type, member) + +#define list_next(elm, member) \ + list_entry((elm)->member.next, typeof(*elm), member) + +#define list_for_each_entry(pos, list, member) \ + for (pos = list_head(list, typeof(*pos), member); \ + &pos->member != (list); \ + pos = list_next(pos, member)) + diff --git a/kernel/tools/firewire/nosy-dump.c b/kernel/tools/firewire/nosy-dump.c new file mode 100644 index 000000000..3179c711b --- /dev/null +++ b/kernel/tools/firewire/nosy-dump.c @@ -0,0 +1,1035 @@ +/* + * nosy-dump - Interface to snoop mode driver for TI PCILynx 1394 controllers + * Copyright (C) 2002-2006 Kristian Høgsberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "nosy-dump.h" +#include "nosy-user.h" + +enum { + PACKET_FIELD_DETAIL = 0x01, + PACKET_FIELD_DATA_LENGTH = 0x02, + /* Marks the fields we print in transaction view. */ + PACKET_FIELD_TRANSACTION = 0x04, +}; + +static void print_packet(uint32_t *data, size_t length); +static void decode_link_packet(struct link_packet *packet, size_t length, + int include_flags, int exclude_flags); +static int run = 1; +sig_t sys_sigint_handler; + +static char *option_nosy_device = "/dev/nosy"; +static char *option_view = "packet"; +static char *option_output; +static char *option_input; +static int option_hex; +static int option_iso; +static int option_cycle_start; +static int option_version; +static int option_verbose; + +enum { + VIEW_TRANSACTION, + VIEW_PACKET, + VIEW_STATS, +}; + +static const struct poptOption options[] = { + { + .longName = "device", + .shortName = 'd', + .argInfo = POPT_ARG_STRING, + .arg = &option_nosy_device, + .descrip = "Path to nosy device.", + .argDescrip = "DEVICE" + }, + { + .longName = "view", + .argInfo = POPT_ARG_STRING, + .arg = &option_view, + .descrip = "Specify view of bus traffic: packet, transaction or stats.", + .argDescrip = "VIEW" + }, + { + .longName = "hex", + .shortName = 'x', + .argInfo = POPT_ARG_NONE, + .arg = &option_hex, + .descrip = "Print each packet in hex.", + }, + { + .longName = "iso", + .argInfo = POPT_ARG_NONE, + .arg = &option_iso, + .descrip = "Print iso packets.", + }, + { + .longName = "cycle-start", + .argInfo = POPT_ARG_NONE, + .arg = &option_cycle_start, + .descrip = "Print cycle start packets.", + }, + { + .longName = "verbose", + .shortName = 'v', + .argInfo = POPT_ARG_NONE, + .arg = &option_verbose, + .descrip = "Verbose packet view.", + }, + { + .longName = "output", + .shortName = 'o', + .argInfo = POPT_ARG_STRING, + .arg = &option_output, + .descrip = "Log to output file.", + .argDescrip = "FILENAME" + }, + { + .longName = "input", + .shortName = 'i', + .argInfo = POPT_ARG_STRING, + .arg = &option_input, + .descrip = "Decode log from file.", + .argDescrip = "FILENAME" + }, + { + .longName = "version", + .argInfo = POPT_ARG_NONE, + .arg = &option_version, + .descrip = "Specify print version info.", + }, + POPT_AUTOHELP + POPT_TABLEEND +}; + +/* Allow all ^C except the first to interrupt the program in the usual way. */ +static void +sigint_handler(int signal_num) +{ + if (run == 1) { + run = 0; + signal(SIGINT, SIG_DFL); + } +} + +static struct subaction * +subaction_create(uint32_t *data, size_t length) +{ + struct subaction *sa; + + /* we put the ack in the subaction struct for easy access. */ + sa = malloc(sizeof *sa - sizeof sa->packet + length); + if (!sa) + exit(EXIT_FAILURE); + sa->ack = data[length / 4 - 1]; + sa->length = length; + memcpy(&sa->packet, data, length); + + return sa; +} + +static void +subaction_destroy(struct subaction *sa) +{ + free(sa); +} + +static struct list pending_transaction_list = { + &pending_transaction_list, &pending_transaction_list +}; + +static struct link_transaction * +link_transaction_lookup(int request_node, int response_node, int tlabel) +{ + struct link_transaction *t; + + list_for_each_entry(t, &pending_transaction_list, link) { + if (t->request_node == request_node && + t->response_node == response_node && + t->tlabel == tlabel) + return t; + } + + t = malloc(sizeof *t); + if (!t) + exit(EXIT_FAILURE); + t->request_node = request_node; + t->response_node = response_node; + t->tlabel = tlabel; + list_init(&t->request_list); + list_init(&t->response_list); + + list_append(&pending_transaction_list, &t->link); + + return t; +} + +static void +link_transaction_destroy(struct link_transaction *t) +{ + struct subaction *sa; + + while (!list_empty(&t->request_list)) { + sa = list_head(&t->request_list, struct subaction, link); + list_remove(&sa->link); + subaction_destroy(sa); + } + while (!list_empty(&t->response_list)) { + sa = list_head(&t->response_list, struct subaction, link); + list_remove(&sa->link); + subaction_destroy(sa); + } + free(t); +} + +struct protocol_decoder { + const char *name; + int (*decode)(struct link_transaction *t); +}; + +static const struct protocol_decoder protocol_decoders[] = { + { "FCP", decode_fcp } +}; + +static void +handle_transaction(struct link_transaction *t) +{ + struct subaction *sa; + int i; + + if (!t->request) { + printf("BUG in handle_transaction\n"); + return; + } + + for (i = 0; i < array_length(protocol_decoders); i++) + if (protocol_decoders[i].decode(t)) + break; + + /* HACK: decode only fcp right now. */ + return; + + decode_link_packet(&t->request->packet, t->request->length, + PACKET_FIELD_TRANSACTION, 0); + if (t->response) + decode_link_packet(&t->response->packet, t->request->length, + PACKET_FIELD_TRANSACTION, 0); + else + printf("[no response]"); + + if (option_verbose) { + list_for_each_entry(sa, &t->request_list, link) + print_packet((uint32_t *) &sa->packet, sa->length); + list_for_each_entry(sa, &t->response_list, link) + print_packet((uint32_t *) &sa->packet, sa->length); + } + printf("\r\n"); + + link_transaction_destroy(t); +} + +static void +clear_pending_transaction_list(void) +{ + struct link_transaction *t; + + while (!list_empty(&pending_transaction_list)) { + t = list_head(&pending_transaction_list, + struct link_transaction, link); + list_remove(&t->link); + link_transaction_destroy(t); + /* print unfinished transactions */ + } +} + +static const char * const tcode_names[] = { + [0x0] = "write_quadlet_request", [0x6] = "read_quadlet_response", + [0x1] = "write_block_request", [0x7] = "read_block_response", + [0x2] = "write_response", [0x8] = "cycle_start", + [0x3] = "reserved", [0x9] = "lock_request", + [0x4] = "read_quadlet_request", [0xa] = "iso_data", + [0x5] = "read_block_request", [0xb] = "lock_response", +}; + +static const char * const ack_names[] = { + [0x0] = "no ack", [0x8] = "reserved (0x08)", + [0x1] = "ack_complete", [0x9] = "reserved (0x09)", + [0x2] = "ack_pending", [0xa] = "reserved (0x0a)", + [0x3] = "reserved (0x03)", [0xb] = "reserved (0x0b)", + [0x4] = "ack_busy_x", [0xc] = "reserved (0x0c)", + [0x5] = "ack_busy_a", [0xd] = "ack_data_error", + [0x6] = "ack_busy_b", [0xe] = "ack_type_error", + [0x7] = "reserved (0x07)", [0xf] = "reserved (0x0f)", +}; + +static const char * const rcode_names[] = { + [0x0] = "complete", [0x4] = "conflict_error", + [0x1] = "reserved (0x01)", [0x5] = "data_error", + [0x2] = "reserved (0x02)", [0x6] = "type_error", + [0x3] = "reserved (0x03)", [0x7] = "address_error", +}; + +static const char * const retry_names[] = { + [0x0] = "retry_1", + [0x1] = "retry_x", + [0x2] = "retry_a", + [0x3] = "retry_b", +}; + +enum { + PACKET_RESERVED, + PACKET_REQUEST, + PACKET_RESPONSE, + PACKET_OTHER, +}; + +struct packet_info { + const char *name; + int type; + int response_tcode; + const struct packet_field *fields; + int field_count; +}; + +struct packet_field { + const char *name; /* Short name for field. */ + int offset; /* Location of field, specified in bits; */ + /* negative means from end of packet. */ + int width; /* Width of field, 0 means use data_length. */ + int flags; /* Show options. */ + const char * const *value_names; +}; + +#define COMMON_REQUEST_FIELDS \ + { "dest", 0, 16, PACKET_FIELD_TRANSACTION }, \ + { "tl", 16, 6 }, \ + { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \ + { "tcode", 24, 4, PACKET_FIELD_TRANSACTION, tcode_names }, \ + { "pri", 28, 4, PACKET_FIELD_DETAIL }, \ + { "src", 32, 16, PACKET_FIELD_TRANSACTION }, \ + { "offs", 48, 48, PACKET_FIELD_TRANSACTION } + +#define COMMON_RESPONSE_FIELDS \ + { "dest", 0, 16 }, \ + { "tl", 16, 6 }, \ + { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \ + { "tcode", 24, 4, 0, tcode_names }, \ + { "pri", 28, 4, PACKET_FIELD_DETAIL }, \ + { "src", 32, 16 }, \ + { "rcode", 48, 4, PACKET_FIELD_TRANSACTION, rcode_names } + +static const struct packet_field read_quadlet_request_fields[] = { + COMMON_REQUEST_FIELDS, + { "crc", 96, 32, PACKET_FIELD_DETAIL }, + { "ack", 156, 4, 0, ack_names }, +}; + +static const struct packet_field read_quadlet_response_fields[] = { + COMMON_RESPONSE_FIELDS, + { "data", 96, 32, PACKET_FIELD_TRANSACTION }, + { "crc", 128, 32, PACKET_FIELD_DETAIL }, + { "ack", 188, 4, 0, ack_names }, +}; + +static const struct packet_field read_block_request_fields[] = { + COMMON_REQUEST_FIELDS, + { "data_length", 96, 16, PACKET_FIELD_TRANSACTION }, + { "extended_tcode", 112, 16 }, + { "crc", 128, 32, PACKET_FIELD_DETAIL }, + { "ack", 188, 4, 0, ack_names }, +}; + +static const struct packet_field block_response_fields[] = { + COMMON_RESPONSE_FIELDS, + { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH }, + { "extended_tcode", 112, 16 }, + { "crc", 128, 32, PACKET_FIELD_DETAIL }, + { "data", 160, 0, PACKET_FIELD_TRANSACTION }, + { "crc", -64, 32, PACKET_FIELD_DETAIL }, + { "ack", -4, 4, 0, ack_names }, +}; + +static const struct packet_field write_quadlet_request_fields[] = { + COMMON_REQUEST_FIELDS, + { "data", 96, 32, PACKET_FIELD_TRANSACTION }, + { "ack", -4, 4, 0, ack_names }, +}; + +static const struct packet_field block_request_fields[] = { + COMMON_REQUEST_FIELDS, + { "data_length", 96, 16, PACKET_FIELD_DATA_LENGTH | PACKET_FIELD_TRANSACTION }, + { "extended_tcode", 112, 16, PACKET_FIELD_TRANSACTION }, + { "crc", 128, 32, PACKET_FIELD_DETAIL }, + { "data", 160, 0, PACKET_FIELD_TRANSACTION }, + { "crc", -64, 32, PACKET_FIELD_DETAIL }, + { "ack", -4, 4, 0, ack_names }, +}; + +static const struct packet_field write_response_fields[] = { + COMMON_RESPONSE_FIELDS, + { "reserved", 64, 32, PACKET_FIELD_DETAIL }, + { "ack", -4, 4, 0, ack_names }, +}; + +static const struct packet_field iso_data_fields[] = { + { "data_length", 0, 16, PACKET_FIELD_DATA_LENGTH }, + { "tag", 16, 2 }, + { "channel", 18, 6 }, + { "tcode", 24, 4, 0, tcode_names }, + { "sy", 28, 4 }, + { "crc", 32, 32, PACKET_FIELD_DETAIL }, + { "data", 64, 0 }, + { "crc", -64, 32, PACKET_FIELD_DETAIL }, + { "ack", -4, 4, 0, ack_names }, +}; + +static const struct packet_info packet_info[] = { + { + .name = "write_quadlet_request", + .type = PACKET_REQUEST, + .response_tcode = TCODE_WRITE_RESPONSE, + .fields = write_quadlet_request_fields, + .field_count = array_length(write_quadlet_request_fields) + }, + { + .name = "write_block_request", + .type = PACKET_REQUEST, + .response_tcode = TCODE_WRITE_RESPONSE, + .fields = block_request_fields, + .field_count = array_length(block_request_fields) + }, + { + .name = "write_response", + .type = PACKET_RESPONSE, + .fields = write_response_fields, + .field_count = array_length(write_response_fields) + }, + { + .name = "reserved", + .type = PACKET_RESERVED, + }, + { + .name = "read_quadlet_request", + .type = PACKET_REQUEST, + .response_tcode = TCODE_READ_QUADLET_RESPONSE, + .fields = read_quadlet_request_fields, + .field_count = array_length(read_quadlet_request_fields) + }, + { + .name = "read_block_request", + .type = PACKET_REQUEST, + .response_tcode = TCODE_READ_BLOCK_RESPONSE, + .fields = read_block_request_fields, + .field_count = array_length(read_block_request_fields) + }, + { + .name = "read_quadlet_response", + .type = PACKET_RESPONSE, + .fields = read_quadlet_response_fields, + .field_count = array_length(read_quadlet_response_fields) + }, + { + .name = "read_block_response", + .type = PACKET_RESPONSE, + .fields = block_response_fields, + .field_count = array_length(block_response_fields) + }, + { + .name = "cycle_start", + .type = PACKET_OTHER, + .fields = write_quadlet_request_fields, + .field_count = array_length(write_quadlet_request_fields) + }, + { + .name = "lock_request", + .type = PACKET_REQUEST, + .fields = block_request_fields, + .field_count = array_length(block_request_fields) + }, + { + .name = "iso_data", + .type = PACKET_OTHER, + .fields = iso_data_fields, + .field_count = array_length(iso_data_fields) + }, + { + .name = "lock_response", + .type = PACKET_RESPONSE, + .fields = block_response_fields, + .field_count = array_length(block_response_fields) + }, +}; + +static int +handle_request_packet(uint32_t *data, size_t length) +{ + struct link_packet *p = (struct link_packet *) data; + struct subaction *sa, *prev; + struct link_transaction *t; + + t = link_transaction_lookup(p->common.source, p->common.destination, + p->common.tlabel); + sa = subaction_create(data, length); + t->request = sa; + + if (!list_empty(&t->request_list)) { + prev = list_tail(&t->request_list, + struct subaction, link); + + if (!ACK_BUSY(prev->ack)) { + /* + * error, we should only see ack_busy_* before the + * ack_pending/ack_complete -- this is an ack_pending + * instead (ack_complete would have finished the + * transaction). + */ + } + + if (prev->packet.common.tcode != sa->packet.common.tcode || + prev->packet.common.tlabel != sa->packet.common.tlabel) { + /* memcmp() ? */ + /* error, these should match for retries. */ + } + } + + list_append(&t->request_list, &sa->link); + + switch (sa->ack) { + case ACK_COMPLETE: + if (p->common.tcode != TCODE_WRITE_QUADLET_REQUEST && + p->common.tcode != TCODE_WRITE_BLOCK_REQUEST) + /* error, unified transactions only allowed for write */; + list_remove(&t->link); + handle_transaction(t); + break; + + case ACK_NO_ACK: + case ACK_DATA_ERROR: + case ACK_TYPE_ERROR: + list_remove(&t->link); + handle_transaction(t); + break; + + case ACK_PENDING: + /* request subaction phase over, wait for response. */ + break; + + case ACK_BUSY_X: + case ACK_BUSY_A: + case ACK_BUSY_B: + /* ok, wait for retry. */ + /* check that retry protocol is respected. */ + break; + } + + return 1; +} + +static int +handle_response_packet(uint32_t *data, size_t length) +{ + struct link_packet *p = (struct link_packet *) data; + struct subaction *sa, *prev; + struct link_transaction *t; + + t = link_transaction_lookup(p->common.destination, p->common.source, + p->common.tlabel); + if (list_empty(&t->request_list)) { + /* unsolicited response */ + } + + sa = subaction_create(data, length); + t->response = sa; + + if (!list_empty(&t->response_list)) { + prev = list_tail(&t->response_list, struct subaction, link); + + if (!ACK_BUSY(prev->ack)) { + /* + * error, we should only see ack_busy_* before the + * ack_pending/ack_complete + */ + } + + if (prev->packet.common.tcode != sa->packet.common.tcode || + prev->packet.common.tlabel != sa->packet.common.tlabel) { + /* use memcmp() instead? */ + /* error, these should match for retries. */ + } + } else { + prev = list_tail(&t->request_list, struct subaction, link); + if (prev->ack != ACK_PENDING) { + /* + * error, should not get response unless last request got + * ack_pending. + */ + } + + if (packet_info[prev->packet.common.tcode].response_tcode != + sa->packet.common.tcode) { + /* error, tcode mismatch */ + } + } + + list_append(&t->response_list, &sa->link); + + switch (sa->ack) { + case ACK_COMPLETE: + case ACK_NO_ACK: + case ACK_DATA_ERROR: + case ACK_TYPE_ERROR: + list_remove(&t->link); + handle_transaction(t); + /* transaction complete, remove t from pending list. */ + break; + + case ACK_PENDING: + /* error for responses. */ + break; + + case ACK_BUSY_X: + case ACK_BUSY_A: + case ACK_BUSY_B: + /* no problem, wait for next retry */ + break; + } + + return 1; +} + +static int +handle_packet(uint32_t *data, size_t length) +{ + if (length == 0) { + printf("bus reset\r\n"); + clear_pending_transaction_list(); + } else if (length > sizeof(struct phy_packet)) { + struct link_packet *p = (struct link_packet *) data; + + switch (packet_info[p->common.tcode].type) { + case PACKET_REQUEST: + return handle_request_packet(data, length); + + case PACKET_RESPONSE: + return handle_response_packet(data, length); + + case PACKET_OTHER: + case PACKET_RESERVED: + return 0; + } + } + + return 1; +} + +static unsigned int +get_bits(struct link_packet *packet, int offset, int width) +{ + uint32_t *data = (uint32_t *) packet; + uint32_t index, shift, mask; + + index = offset / 32 + 1; + shift = 32 - (offset & 31) - width; + mask = width == 32 ? ~0 : (1 << width) - 1; + + return (data[index] >> shift) & mask; +} + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define byte_index(i) ((i) ^ 3) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define byte_index(i) (i) +#else +#error unsupported byte order. +#endif + +static void +dump_data(unsigned char *data, int length) +{ + int i, print_length; + + if (length > 128) + print_length = 128; + else + print_length = length; + + for (i = 0; i < print_length; i++) + printf("%s%02hhx", + (i % 4 == 0 && i != 0) ? " " : "", + data[byte_index(i)]); + + if (print_length < length) + printf(" (%d more bytes)", length - print_length); +} + +static void +decode_link_packet(struct link_packet *packet, size_t length, + int include_flags, int exclude_flags) +{ + const struct packet_info *pi; + int data_length = 0; + int i; + + pi = &packet_info[packet->common.tcode]; + + for (i = 0; i < pi->field_count; i++) { + const struct packet_field *f = &pi->fields[i]; + int offset; + + if (f->flags & exclude_flags) + continue; + if (include_flags && !(f->flags & include_flags)) + continue; + + if (f->offset < 0) + offset = length * 8 + f->offset - 32; + else + offset = f->offset; + + if (f->value_names != NULL) { + uint32_t bits; + + bits = get_bits(packet, offset, f->width); + printf("%s", f->value_names[bits]); + } else if (f->width == 0) { + printf("%s=[", f->name); + dump_data((unsigned char *) packet + (offset / 8 + 4), data_length); + printf("]"); + } else { + unsigned long long bits; + int high_width, low_width; + + if ((offset & ~31) != ((offset + f->width - 1) & ~31)) { + /* Bit field spans quadlet boundary. */ + high_width = ((offset + 31) & ~31) - offset; + low_width = f->width - high_width; + + bits = get_bits(packet, offset, high_width); + bits = (bits << low_width) | + get_bits(packet, offset + high_width, low_width); + } else { + bits = get_bits(packet, offset, f->width); + } + + printf("%s=0x%0*llx", f->name, (f->width + 3) / 4, bits); + + if (f->flags & PACKET_FIELD_DATA_LENGTH) + data_length = bits; + } + + if (i < pi->field_count - 1) + printf(", "); + } +} + +static void +print_packet(uint32_t *data, size_t length) +{ + int i; + + printf("%6u ", data[0]); + + if (length == 4) { + printf("bus reset"); + } else if (length < sizeof(struct phy_packet)) { + printf("short packet: "); + for (i = 1; i < length / 4; i++) + printf("%s%08x", i == 0 ? "[" : " ", data[i]); + printf("]"); + + } else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) { + struct phy_packet *pp = (struct phy_packet *) data; + + /* phy packet are 3 quadlets: the 1 quadlet payload, + * the bitwise inverse of the payload and the snoop + * mode ack */ + + switch (pp->common.identifier) { + case PHY_PACKET_CONFIGURATION: + if (!pp->phy_config.set_root && !pp->phy_config.set_gap_count) { + printf("ext phy config: phy_id=%02x", pp->phy_config.root_id); + } else { + printf("phy config:"); + if (pp->phy_config.set_root) + printf(" set_root_id=%02x", pp->phy_config.root_id); + if (pp->phy_config.set_gap_count) + printf(" set_gap_count=%d", pp->phy_config.gap_count); + } + break; + + case PHY_PACKET_LINK_ON: + printf("link-on packet, phy_id=%02x", pp->link_on.phy_id); + break; + + case PHY_PACKET_SELF_ID: + if (pp->self_id.extended) { + printf("extended self id: phy_id=%02x, seq=%d", + pp->ext_self_id.phy_id, pp->ext_self_id.sequence); + } else { + static const char * const speed_names[] = { + "S100", "S200", "S400", "BETA" + }; + printf("self id: phy_id=%02x, link %s, gap_count=%d, speed=%s%s%s", + pp->self_id.phy_id, + (pp->self_id.link_active ? "active" : "not active"), + pp->self_id.gap_count, + speed_names[pp->self_id.phy_speed], + (pp->self_id.contender ? ", irm contender" : ""), + (pp->self_id.initiated_reset ? ", initiator" : "")); + } + break; + default: + printf("unknown phy packet: "); + for (i = 1; i < length / 4; i++) + printf("%s%08x", i == 0 ? "[" : " ", data[i]); + printf("]"); + break; + } + } else { + struct link_packet *packet = (struct link_packet *) data; + + decode_link_packet(packet, length, 0, + option_verbose ? 0 : PACKET_FIELD_DETAIL); + } + + if (option_hex) { + printf(" ["); + dump_data((unsigned char *) data + 4, length - 4); + printf("]"); + } + + printf("\r\n"); +} + +#define HIDE_CURSOR "\033[?25l" +#define SHOW_CURSOR "\033[?25h" +#define CLEAR "\033[H\033[2J" + +static void +print_stats(uint32_t *data, size_t length) +{ + static int bus_reset_count, short_packet_count, phy_packet_count; + static int tcode_count[16]; + static struct timeval last_update; + struct timeval now; + int i; + + if (length == 0) + bus_reset_count++; + else if (length < sizeof(struct phy_packet)) + short_packet_count++; + else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) + phy_packet_count++; + else { + struct link_packet *packet = (struct link_packet *) data; + tcode_count[packet->common.tcode]++; + } + + gettimeofday(&now, NULL); + if (now.tv_sec <= last_update.tv_sec && + now.tv_usec < last_update.tv_usec + 500000) + return; + + last_update = now; + printf(CLEAR HIDE_CURSOR + " bus resets : %8d\n" + " short packets : %8d\n" + " phy packets : %8d\n", + bus_reset_count, short_packet_count, phy_packet_count); + + for (i = 0; i < array_length(packet_info); i++) + if (packet_info[i].type != PACKET_RESERVED) + printf(" %-24s: %8d\n", packet_info[i].name, tcode_count[i]); + printf(SHOW_CURSOR "\n"); +} + +static struct termios saved_attributes; + +static void +reset_input_mode(void) +{ + tcsetattr(STDIN_FILENO, TCSANOW, &saved_attributes); +} + +static void +set_input_mode(void) +{ + struct termios tattr; + + /* Make sure stdin is a terminal. */ + if (!isatty(STDIN_FILENO)) { + fprintf(stderr, "Not a terminal.\n"); + exit(EXIT_FAILURE); + } + + /* Save the terminal attributes so we can restore them later. */ + tcgetattr(STDIN_FILENO, &saved_attributes); + atexit(reset_input_mode); + + /* Set the funny terminal modes. */ + tcgetattr(STDIN_FILENO, &tattr); + tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */ + tattr.c_cc[VMIN] = 1; + tattr.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr); +} + +int main(int argc, const char *argv[]) +{ + uint32_t buf[128 * 1024]; + uint32_t filter; + int length, retval, view; + int fd = -1; + FILE *output = NULL, *input = NULL; + poptContext con; + char c; + struct pollfd pollfds[2]; + + sys_sigint_handler = signal(SIGINT, sigint_handler); + + con = poptGetContext(NULL, argc, argv, options, 0); + retval = poptGetNextOpt(con); + if (retval < -1) { + poptPrintUsage(con, stdout, 0); + return -1; + } + + if (option_version) { + printf("dump tool for nosy sniffer, version %s\n", VERSION); + return 0; + } + + if (__BYTE_ORDER != __LITTLE_ENDIAN) + fprintf(stderr, "warning: nosy has only been tested on little " + "endian machines\n"); + + if (option_input != NULL) { + input = fopen(option_input, "r"); + if (input == NULL) { + fprintf(stderr, "Could not open %s, %m\n", option_input); + return -1; + } + } else { + fd = open(option_nosy_device, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Could not open %s, %m\n", option_nosy_device); + return -1; + } + set_input_mode(); + } + + if (strcmp(option_view, "transaction") == 0) + view = VIEW_TRANSACTION; + else if (strcmp(option_view, "stats") == 0) + view = VIEW_STATS; + else + view = VIEW_PACKET; + + if (option_output) { + output = fopen(option_output, "w"); + if (output == NULL) { + fprintf(stderr, "Could not open %s, %m\n", option_output); + return -1; + } + } + + setvbuf(stdout, NULL, _IOLBF, BUFSIZ); + + filter = ~0; + if (!option_iso) + filter &= ~(1 << TCODE_STREAM_DATA); + if (!option_cycle_start) + filter &= ~(1 << TCODE_CYCLE_START); + if (view == VIEW_STATS) + filter = ~(1 << TCODE_CYCLE_START); + + ioctl(fd, NOSY_IOC_FILTER, filter); + + ioctl(fd, NOSY_IOC_START); + + pollfds[0].fd = fd; + pollfds[0].events = POLLIN; + pollfds[1].fd = STDIN_FILENO; + pollfds[1].events = POLLIN; + + while (run) { + if (input != NULL) { + if (fread(&length, sizeof length, 1, input) != 1) + return 0; + fread(buf, 1, length, input); + } else { + poll(pollfds, 2, -1); + if (pollfds[1].revents) { + read(STDIN_FILENO, &c, sizeof c); + switch (c) { + case 'q': + if (output != NULL) + fclose(output); + return 0; + } + } + + if (pollfds[0].revents) + length = read(fd, buf, sizeof buf); + else + continue; + } + + if (output != NULL) { + fwrite(&length, sizeof length, 1, output); + fwrite(buf, 1, length, output); + } + + switch (view) { + case VIEW_TRANSACTION: + handle_packet(buf, length); + break; + case VIEW_PACKET: + print_packet(buf, length); + break; + case VIEW_STATS: + print_stats(buf, length); + break; + } + } + + if (output != NULL) + fclose(output); + + close(fd); + + poptFreeContext(con); + + return 0; +} diff --git a/kernel/tools/firewire/nosy-dump.h b/kernel/tools/firewire/nosy-dump.h new file mode 100644 index 000000000..3a4b5b33b --- /dev/null +++ b/kernel/tools/firewire/nosy-dump.h @@ -0,0 +1,173 @@ +#ifndef __nosy_dump_h__ +#define __nosy_dump_h__ + +#define array_length(array) (sizeof(array) / sizeof(array[0])) + +#define ACK_NO_ACK 0x0 +#define ACK_DONE(a) ((a >> 2) == 0) +#define ACK_BUSY(a) ((a >> 2) == 1) +#define ACK_ERROR(a) ((a >> 2) == 3) + +#include + +struct phy_packet { + uint32_t timestamp; + union { + struct { + uint32_t zero:24; + uint32_t phy_id:6; + uint32_t identifier:2; + } common, link_on; + + struct { + uint32_t zero:16; + uint32_t gap_count:6; + uint32_t set_gap_count:1; + uint32_t set_root:1; + uint32_t root_id:6; + uint32_t identifier:2; + } phy_config; + + struct { + uint32_t more_packets:1; + uint32_t initiated_reset:1; + uint32_t port2:2; + uint32_t port1:2; + uint32_t port0:2; + uint32_t power_class:3; + uint32_t contender:1; + uint32_t phy_delay:2; + uint32_t phy_speed:2; + uint32_t gap_count:6; + uint32_t link_active:1; + uint32_t extended:1; + uint32_t phy_id:6; + uint32_t identifier:2; + } self_id; + + struct { + uint32_t more_packets:1; + uint32_t reserved1:1; + uint32_t porth:2; + uint32_t portg:2; + uint32_t portf:2; + uint32_t porte:2; + uint32_t portd:2; + uint32_t portc:2; + uint32_t portb:2; + uint32_t porta:2; + uint32_t reserved0:2; + uint32_t sequence:3; + uint32_t extended:1; + uint32_t phy_id:6; + uint32_t identifier:2; + } ext_self_id; + }; + uint32_t inverted; + uint32_t ack; +}; + +#define TCODE_PHY_PACKET 0x10 + +#define PHY_PACKET_CONFIGURATION 0x00 +#define PHY_PACKET_LINK_ON 0x01 +#define PHY_PACKET_SELF_ID 0x02 + +struct link_packet { + uint32_t timestamp; + union { + struct { + uint32_t priority:4; + uint32_t tcode:4; + uint32_t rt:2; + uint32_t tlabel:6; + uint32_t destination:16; + + uint32_t offset_high:16; + uint32_t source:16; + + uint32_t offset_low; + } common; + + struct { + uint32_t common[3]; + uint32_t crc; + } read_quadlet; + + struct { + uint32_t common[3]; + uint32_t data; + uint32_t crc; + } read_quadlet_response; + + struct { + uint32_t common[3]; + uint32_t extended_tcode:16; + uint32_t data_length:16; + uint32_t crc; + } read_block; + + struct { + uint32_t common[3]; + uint32_t extended_tcode:16; + uint32_t data_length:16; + uint32_t crc; + uint32_t data[0]; + /* crc and ack follows. */ + } read_block_response; + + struct { + uint32_t common[3]; + uint32_t data; + uint32_t crc; + } write_quadlet; + + struct { + uint32_t common[3]; + uint32_t extended_tcode:16; + uint32_t data_length:16; + uint32_t crc; + uint32_t data[0]; + /* crc and ack follows. */ + } write_block; + + struct { + uint32_t common[3]; + uint32_t crc; + } write_response; + + struct { + uint32_t common[3]; + uint32_t data; + uint32_t crc; + } cycle_start; + + struct { + uint32_t sy:4; + uint32_t tcode:4; + uint32_t channel:6; + uint32_t tag:2; + uint32_t data_length:16; + + uint32_t crc; + } iso_data; + }; +}; + +struct subaction { + uint32_t ack; + size_t length; + struct list link; + struct link_packet packet; +}; + +struct link_transaction { + int request_node, response_node, tlabel; + struct subaction *request, *response; + struct list request_list, response_list; + struct list link; +}; + +int decode_fcp(struct link_transaction *t); + +#endif /* __nosy_dump_h__ */ diff --git a/kernel/tools/hv/Makefile b/kernel/tools/hv/Makefile new file mode 100644 index 000000000..a8ab79556 --- /dev/null +++ b/kernel/tools/hv/Makefile @@ -0,0 +1,13 @@ +# Makefile for Hyper-V tools + +CC = $(CROSS_COMPILE)gcc +PTHREAD_LIBS = -lpthread +WARNINGS = -Wall -Wextra +CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) $(shell getconf LFS_CFLAGS) + +all: hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +clean: + $(RM) hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon diff --git a/kernel/tools/hv/hv_fcopy_daemon.c b/kernel/tools/hv/hv_fcopy_daemon.c new file mode 100644 index 000000000..9445d8f26 --- /dev/null +++ b/kernel/tools/hv/hv_fcopy_daemon.c @@ -0,0 +1,221 @@ +/* + * An implementation of host to guest copy functionality for Linux. + * + * Copyright (C) 2014, Microsoft, Inc. + * + * Author : K. Y. Srinivasan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int target_fd; +static char target_fname[W_MAX_PATH]; + +static int hv_start_fcopy(struct hv_start_fcopy *smsg) +{ + int error = HV_E_FAIL; + char *q, *p; + + p = (char *)smsg->path_name; + snprintf(target_fname, sizeof(target_fname), "%s/%s", + (char *)smsg->path_name, (char *)smsg->file_name); + + syslog(LOG_INFO, "Target file name: %s", target_fname); + /* + * Check to see if the path is already in place; if not, + * create if required. + */ + while ((q = strchr(p, '/')) != NULL) { + if (q == p) { + p++; + continue; + } + *q = '\0'; + if (access((char *)smsg->path_name, F_OK)) { + if (smsg->copy_flags & CREATE_PATH) { + if (mkdir((char *)smsg->path_name, 0755)) { + syslog(LOG_ERR, "Failed to create %s", + (char *)smsg->path_name); + goto done; + } + } else { + syslog(LOG_ERR, "Invalid path: %s", + (char *)smsg->path_name); + goto done; + } + } + p = q + 1; + *q = '/'; + } + + if (!access(target_fname, F_OK)) { + syslog(LOG_INFO, "File: %s exists", target_fname); + if (!(smsg->copy_flags & OVER_WRITE)) { + error = HV_ERROR_ALREADY_EXISTS; + goto done; + } + } + + target_fd = open(target_fname, + O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744); + if (target_fd == -1) { + syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); + goto done; + } + + error = 0; +done: + return error; +} + +static int hv_copy_data(struct hv_do_fcopy *cpmsg) +{ + ssize_t bytes_written; + + bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size, + cpmsg->offset); + + if (bytes_written != cpmsg->size) + return HV_E_FAIL; + + return 0; +} + +static int hv_copy_finished(void) +{ + close(target_fd); + return 0; +} +static int hv_copy_cancel(void) +{ + close(target_fd); + unlink(target_fname); + return 0; + +} + +void print_usage(char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n" + "Options are:\n" + " -n, --no-daemon stay in foreground, don't daemonize\n" + " -h, --help print this help\n", argv[0]); +} + +int main(int argc, char *argv[]) +{ + int fcopy_fd, len; + int error; + int daemonize = 1, long_index = 0, opt; + int version = FCOPY_CURRENT_VERSION; + char *buffer[4096 * 2]; + struct hv_fcopy_hdr *in_msg; + + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"no-daemon", no_argument, 0, 'n' }, + {0, 0, 0, 0 } + }; + + while ((opt = getopt_long(argc, argv, "hn", long_options, + &long_index)) != -1) { + switch (opt) { + case 'n': + daemonize = 0; + break; + case 'h': + default: + print_usage(argv); + exit(EXIT_FAILURE); + } + } + + if (daemonize && daemon(1, 0)) { + syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + + openlog("HV_FCOPY", 0, LOG_USER); + syslog(LOG_INFO, "HV_FCOPY starting; pid is:%d", getpid()); + + fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR); + + if (fcopy_fd < 0) { + syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s", + errno, strerror(errno)); + exit(EXIT_FAILURE); + } + + /* + * Register with the kernel. + */ + if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) { + syslog(LOG_ERR, "Registration failed: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + + while (1) { + /* + * In this loop we process fcopy messages after the + * handshake is complete. + */ + len = pread(fcopy_fd, buffer, (4096 * 2), 0); + if (len < 0) { + syslog(LOG_ERR, "pread failed: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + in_msg = (struct hv_fcopy_hdr *)buffer; + + switch (in_msg->operation) { + case START_FILE_COPY: + error = hv_start_fcopy((struct hv_start_fcopy *)in_msg); + break; + case WRITE_TO_FILE: + error = hv_copy_data((struct hv_do_fcopy *)in_msg); + break; + case COMPLETE_FCOPY: + error = hv_copy_finished(); + break; + case CANCEL_FCOPY: + error = hv_copy_cancel(); + break; + + default: + syslog(LOG_ERR, "Unknown operation: %d", + in_msg->operation); + + } + + if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) { + syslog(LOG_ERR, "pwrite failed: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + } +} diff --git a/kernel/tools/hv/hv_get_dhcp_info.sh b/kernel/tools/hv/hv_get_dhcp_info.sh new file mode 100755 index 000000000..ccd3e9532 --- /dev/null +++ b/kernel/tools/hv/hv_get_dhcp_info.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# This example script retrieves the DHCP state of a given interface. +# In the interest of keeping the KVP daemon code free of distro specific +# information; the kvp daemon code invokes this external script to gather +# DHCP setting for the specific interface. +# +# Input: Name of the interface +# +# Output: The script prints the string "Enabled" to stdout to indicate +# that DHCP is enabled on the interface. If DHCP is not enabled, +# the script prints the string "Disabled" to stdout. +# +# Each Distro is expected to implement this script in a distro specific +# fashion. For instance on Distros that ship with Network Manager enabled, +# this script can be based on the Network Manager APIs for retrieving DHCP +# information. + +if_file="/etc/sysconfig/network-scripts/ifcfg-"$1 + +dhcp=$(grep "dhcp" $if_file 2>/dev/null) + +if [ "$dhcp" != "" ]; +then +echo "Enabled" +else +echo "Disabled" +fi diff --git a/kernel/tools/hv/hv_get_dns_info.sh b/kernel/tools/hv/hv_get_dns_info.sh new file mode 100755 index 000000000..058c17b46 --- /dev/null +++ b/kernel/tools/hv/hv_get_dns_info.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# This example script parses /etc/resolv.conf to retrive DNS information. +# In the interest of keeping the KVP daemon code free of distro specific +# information; the kvp daemon code invokes this external script to gather +# DNS information. +# This script is expected to print the nameserver values to stdout. +# Each Distro is expected to implement this script in a distro specific +# fashion. For instance on Distros that ship with Network Manager enabled, +# this script can be based on the Network Manager APIs for retrieving DNS +# entries. + +cat /etc/resolv.conf 2>/dev/null | awk '/^nameserver/ { print $2 }' diff --git a/kernel/tools/hv/hv_kvp_daemon.c b/kernel/tools/hv/hv_kvp_daemon.c new file mode 100644 index 000000000..408bb076a --- /dev/null +++ b/kernel/tools/hv/hv_kvp_daemon.c @@ -0,0 +1,1783 @@ +/* + * An implementation of key value pair (KVP) functionality for Linux. + * + * + * Copyright (C) 2010, Novell, Inc. + * Author : K. Y. Srinivasan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * KVP protocol: The user mode component first registers with the + * the kernel component. Subsequently, the kernel component requests, data + * for the specified keys. In response to this message the user mode component + * fills in the value corresponding to the specified key. We overload the + * sequence field in the cn_msg header to define our KVP message types. + * + * We use this infrastructure for also supporting queries from user mode + * application for state that may be maintained in the KVP kernel component. + * + */ + + +enum key_index { + FullyQualifiedDomainName = 0, + IntegrationServicesVersion, /*This key is serviced in the kernel*/ + NetworkAddressIPv4, + NetworkAddressIPv6, + OSBuildNumber, + OSName, + OSMajorVersion, + OSMinorVersion, + OSVersion, + ProcessorArchitecture +}; + + +enum { + IPADDR = 0, + NETMASK, + GATEWAY, + DNS +}; + +static struct sockaddr_nl addr; +static int in_hand_shake = 1; + +static char *os_name = ""; +static char *os_major = ""; +static char *os_minor = ""; +static char *processor_arch; +static char *os_build; +static char *os_version; +static char *lic_version = "Unknown version"; +static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; +static struct utsname uts_buf; + +/* + * The location of the interface configuration file. + */ + +#define KVP_CONFIG_LOC "/var/lib/hyperv" + +#define MAX_FILE_NAME 100 +#define ENTRIES_PER_BLOCK 50 + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +struct kvp_record { + char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; +}; + +struct kvp_file_state { + int fd; + int num_blocks; + struct kvp_record *records; + int num_records; + char fname[MAX_FILE_NAME]; +}; + +static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT]; + +static void kvp_acquire_lock(int pool) +{ + struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0}; + fl.l_pid = getpid(); + + if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) { + syslog(LOG_ERR, "Failed to acquire the lock pool: %d; error: %d %s", pool, + errno, strerror(errno)); + exit(EXIT_FAILURE); + } +} + +static void kvp_release_lock(int pool) +{ + struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0}; + fl.l_pid = getpid(); + + if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) { + syslog(LOG_ERR, "Failed to release the lock pool: %d; error: %d %s", pool, + errno, strerror(errno)); + exit(EXIT_FAILURE); + } +} + +static void kvp_update_file(int pool) +{ + FILE *filep; + + /* + * We are going to write our in-memory registry out to + * disk; acquire the lock first. + */ + kvp_acquire_lock(pool); + + filep = fopen(kvp_file_info[pool].fname, "we"); + if (!filep) { + syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool, + errno, strerror(errno)); + kvp_release_lock(pool); + exit(EXIT_FAILURE); + } + + fwrite(kvp_file_info[pool].records, sizeof(struct kvp_record), + kvp_file_info[pool].num_records, filep); + + if (ferror(filep) || fclose(filep)) { + kvp_release_lock(pool); + syslog(LOG_ERR, "Failed to write file, pool: %d", pool); + exit(EXIT_FAILURE); + } + + kvp_release_lock(pool); +} + +static void kvp_update_mem_state(int pool) +{ + FILE *filep; + size_t records_read = 0; + struct kvp_record *record = kvp_file_info[pool].records; + struct kvp_record *readp; + int num_blocks = kvp_file_info[pool].num_blocks; + int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; + + kvp_acquire_lock(pool); + + filep = fopen(kvp_file_info[pool].fname, "re"); + if (!filep) { + syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool, + errno, strerror(errno)); + kvp_release_lock(pool); + exit(EXIT_FAILURE); + } + for (;;) { + readp = &record[records_read]; + records_read += fread(readp, sizeof(struct kvp_record), + ENTRIES_PER_BLOCK * num_blocks, + filep); + + if (ferror(filep)) { + syslog(LOG_ERR, "Failed to read file, pool: %d", pool); + exit(EXIT_FAILURE); + } + + if (!feof(filep)) { + /* + * We have more data to read. + */ + num_blocks++; + record = realloc(record, alloc_unit * num_blocks); + + if (record == NULL) { + syslog(LOG_ERR, "malloc failed"); + exit(EXIT_FAILURE); + } + continue; + } + break; + } + + kvp_file_info[pool].num_blocks = num_blocks; + kvp_file_info[pool].records = record; + kvp_file_info[pool].num_records = records_read; + + fclose(filep); + kvp_release_lock(pool); +} +static int kvp_file_init(void) +{ + int fd; + FILE *filep; + size_t records_read; + char *fname; + struct kvp_record *record; + struct kvp_record *readp; + int num_blocks; + int i; + int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; + + if (access(KVP_CONFIG_LOC, F_OK)) { + if (mkdir(KVP_CONFIG_LOC, 0755 /* rwxr-xr-x */)) { + syslog(LOG_ERR, "Failed to create '%s'; error: %d %s", KVP_CONFIG_LOC, + errno, strerror(errno)); + exit(EXIT_FAILURE); + } + } + + for (i = 0; i < KVP_POOL_COUNT; i++) { + fname = kvp_file_info[i].fname; + records_read = 0; + num_blocks = 1; + sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i); + fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */); + + if (fd == -1) + return 1; + + + filep = fopen(fname, "re"); + if (!filep) { + close(fd); + return 1; + } + + record = malloc(alloc_unit * num_blocks); + if (record == NULL) { + fclose(filep); + close(fd); + return 1; + } + for (;;) { + readp = &record[records_read]; + records_read += fread(readp, sizeof(struct kvp_record), + ENTRIES_PER_BLOCK, + filep); + + if (ferror(filep)) { + syslog(LOG_ERR, "Failed to read file, pool: %d", + i); + exit(EXIT_FAILURE); + } + + if (!feof(filep)) { + /* + * We have more data to read. + */ + num_blocks++; + record = realloc(record, alloc_unit * + num_blocks); + if (record == NULL) { + fclose(filep); + close(fd); + return 1; + } + continue; + } + break; + } + kvp_file_info[i].fd = fd; + kvp_file_info[i].num_blocks = num_blocks; + kvp_file_info[i].records = record; + kvp_file_info[i].num_records = records_read; + fclose(filep); + + } + + return 0; +} + +static int kvp_key_delete(int pool, const __u8 *key, int key_size) +{ + int i; + int j, k; + int num_records; + struct kvp_record *record; + + /* + * First update the in-memory state. + */ + kvp_update_mem_state(pool); + + num_records = kvp_file_info[pool].num_records; + record = kvp_file_info[pool].records; + + for (i = 0; i < num_records; i++) { + if (memcmp(key, record[i].key, key_size)) + continue; + /* + * Found a match; just move the remaining + * entries up. + */ + if (i == num_records) { + kvp_file_info[pool].num_records--; + kvp_update_file(pool); + return 0; + } + + j = i; + k = j + 1; + for (; k < num_records; k++) { + strcpy(record[j].key, record[k].key); + strcpy(record[j].value, record[k].value); + j++; + } + + kvp_file_info[pool].num_records--; + kvp_update_file(pool); + return 0; + } + return 1; +} + +static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size, + const __u8 *value, int value_size) +{ + int i; + int num_records; + struct kvp_record *record; + int num_blocks; + + if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || + (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) + return 1; + + /* + * First update the in-memory state. + */ + kvp_update_mem_state(pool); + + num_records = kvp_file_info[pool].num_records; + record = kvp_file_info[pool].records; + num_blocks = kvp_file_info[pool].num_blocks; + + for (i = 0; i < num_records; i++) { + if (memcmp(key, record[i].key, key_size)) + continue; + /* + * Found a match; just update the value - + * this is the modify case. + */ + memcpy(record[i].value, value, value_size); + kvp_update_file(pool); + return 0; + } + + /* + * Need to add a new entry; + */ + if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) { + /* Need to allocate a larger array for reg entries. */ + record = realloc(record, sizeof(struct kvp_record) * + ENTRIES_PER_BLOCK * (num_blocks + 1)); + + if (record == NULL) + return 1; + kvp_file_info[pool].num_blocks++; + + } + memcpy(record[i].value, value, value_size); + memcpy(record[i].key, key, key_size); + kvp_file_info[pool].records = record; + kvp_file_info[pool].num_records++; + kvp_update_file(pool); + return 0; +} + +static int kvp_get_value(int pool, const __u8 *key, int key_size, __u8 *value, + int value_size) +{ + int i; + int num_records; + struct kvp_record *record; + + if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || + (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) + return 1; + + /* + * First update the in-memory state. + */ + kvp_update_mem_state(pool); + + num_records = kvp_file_info[pool].num_records; + record = kvp_file_info[pool].records; + + for (i = 0; i < num_records; i++) { + if (memcmp(key, record[i].key, key_size)) + continue; + /* + * Found a match; just copy the value out. + */ + memcpy(value, record[i].value, value_size); + return 0; + } + + return 1; +} + +static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, + __u8 *value, int value_size) +{ + struct kvp_record *record; + + /* + * First update our in-memory database. + */ + kvp_update_mem_state(pool); + record = kvp_file_info[pool].records; + + if (index >= kvp_file_info[pool].num_records) { + return 1; + } + + memcpy(key, record[index].key, key_size); + memcpy(value, record[index].value, value_size); + return 0; +} + + +void kvp_get_os_info(void) +{ + FILE *file; + char *p, buf[512]; + + uname(&uts_buf); + os_version = uts_buf.release; + os_build = strdup(uts_buf.release); + + os_name = uts_buf.sysname; + processor_arch = uts_buf.machine; + + /* + * The current windows host (win7) expects the build + * string to be of the form: x.y.z + * Strip additional information we may have. + */ + p = strchr(os_version, '-'); + if (p) + *p = '\0'; + + /* + * Parse the /etc/os-release file if present: + * http://www.freedesktop.org/software/systemd/man/os-release.html + */ + file = fopen("/etc/os-release", "r"); + if (file != NULL) { + while (fgets(buf, sizeof(buf), file)) { + char *value, *q; + + /* Ignore comments */ + if (buf[0] == '#') + continue; + + /* Split into name=value */ + p = strchr(buf, '='); + if (!p) + continue; + *p++ = 0; + + /* Remove quotes and newline; un-escape */ + value = p; + q = p; + while (*p) { + if (*p == '\\') { + ++p; + if (!*p) + break; + *q++ = *p++; + } else if (*p == '\'' || *p == '"' || + *p == '\n') { + ++p; + } else { + *q++ = *p++; + } + } + *q = 0; + + if (!strcmp(buf, "NAME")) { + p = strdup(value); + if (!p) + break; + os_name = p; + } else if (!strcmp(buf, "VERSION_ID")) { + p = strdup(value); + if (!p) + break; + os_major = p; + } + } + fclose(file); + return; + } + + /* Fallback for older RH/SUSE releases */ + file = fopen("/etc/SuSE-release", "r"); + if (file != NULL) + goto kvp_osinfo_found; + file = fopen("/etc/redhat-release", "r"); + if (file != NULL) + goto kvp_osinfo_found; + + /* + * We don't have information about the os. + */ + return; + +kvp_osinfo_found: + /* up to three lines */ + p = fgets(buf, sizeof(buf), file); + if (p) { + p = strchr(buf, '\n'); + if (p) + *p = '\0'; + p = strdup(buf); + if (!p) + goto done; + os_name = p; + + /* second line */ + p = fgets(buf, sizeof(buf), file); + if (p) { + p = strchr(buf, '\n'); + if (p) + *p = '\0'; + p = strdup(buf); + if (!p) + goto done; + os_major = p; + + /* third line */ + p = fgets(buf, sizeof(buf), file); + if (p) { + p = strchr(buf, '\n'); + if (p) + *p = '\0'; + p = strdup(buf); + if (p) + os_minor = p; + } + } + } + +done: + fclose(file); + return; +} + + + +/* + * Retrieve an interface name corresponding to the specified guid. + * If there is a match, the function returns a pointer + * to the interface name and if not, a NULL is returned. + * If a match is found, the caller is responsible for + * freeing the memory. + */ + +static char *kvp_get_if_name(char *guid) +{ + DIR *dir; + struct dirent *entry; + FILE *file; + char *p, *q, *x; + char *if_name = NULL; + char buf[256]; + char *kvp_net_dir = "/sys/class/net/"; + char dev_id[256]; + + dir = opendir(kvp_net_dir); + if (dir == NULL) + return NULL; + + snprintf(dev_id, sizeof(dev_id), "%s", kvp_net_dir); + q = dev_id + strlen(kvp_net_dir); + + while ((entry = readdir(dir)) != NULL) { + /* + * Set the state for the next pass. + */ + *q = '\0'; + strcat(dev_id, entry->d_name); + strcat(dev_id, "/device/device_id"); + + file = fopen(dev_id, "r"); + if (file == NULL) + continue; + + p = fgets(buf, sizeof(buf), file); + if (p) { + x = strchr(p, '\n'); + if (x) + *x = '\0'; + + if (!strcmp(p, guid)) { + /* + * Found the guid match; return the interface + * name. The caller will free the memory. + */ + if_name = strdup(entry->d_name); + fclose(file); + break; + } + } + fclose(file); + } + + closedir(dir); + return if_name; +} + +/* + * Retrieve the MAC address given the interface name. + */ + +static char *kvp_if_name_to_mac(char *if_name) +{ + FILE *file; + char *p, *x; + char buf[256]; + char addr_file[256]; + unsigned int i; + char *mac_addr = NULL; + + snprintf(addr_file, sizeof(addr_file), "%s%s%s", "/sys/class/net/", + if_name, "/address"); + + file = fopen(addr_file, "r"); + if (file == NULL) + return NULL; + + p = fgets(buf, sizeof(buf), file); + if (p) { + x = strchr(p, '\n'); + if (x) + *x = '\0'; + for (i = 0; i < strlen(p); i++) + p[i] = toupper(p[i]); + mac_addr = strdup(p); + } + + fclose(file); + return mac_addr; +} + + +/* + * Retrieve the interface name given tha MAC address. + */ + +static char *kvp_mac_to_if_name(char *mac) +{ + DIR *dir; + struct dirent *entry; + FILE *file; + char *p, *q, *x; + char *if_name = NULL; + char buf[256]; + char *kvp_net_dir = "/sys/class/net/"; + char dev_id[256]; + unsigned int i; + + dir = opendir(kvp_net_dir); + if (dir == NULL) + return NULL; + + snprintf(dev_id, sizeof(dev_id), kvp_net_dir); + q = dev_id + strlen(kvp_net_dir); + + while ((entry = readdir(dir)) != NULL) { + /* + * Set the state for the next pass. + */ + *q = '\0'; + + strcat(dev_id, entry->d_name); + strcat(dev_id, "/address"); + + file = fopen(dev_id, "r"); + if (file == NULL) + continue; + + p = fgets(buf, sizeof(buf), file); + if (p) { + x = strchr(p, '\n'); + if (x) + *x = '\0'; + + for (i = 0; i < strlen(p); i++) + p[i] = toupper(p[i]); + + if (!strcmp(p, mac)) { + /* + * Found the MAC match; return the interface + * name. The caller will free the memory. + */ + if_name = strdup(entry->d_name); + fclose(file); + break; + } + } + fclose(file); + } + + closedir(dir); + return if_name; +} + + +static void kvp_process_ipconfig_file(char *cmd, + char *config_buf, unsigned int len, + int element_size, int offset) +{ + char buf[256]; + char *p; + char *x; + FILE *file; + + /* + * First execute the command. + */ + file = popen(cmd, "r"); + if (file == NULL) + return; + + if (offset == 0) + memset(config_buf, 0, len); + while ((p = fgets(buf, sizeof(buf), file)) != NULL) { + if (len < strlen(config_buf) + element_size + 1) + break; + + x = strchr(p, '\n'); + if (x) + *x = '\0'; + + strcat(config_buf, p); + strcat(config_buf, ";"); + } + pclose(file); +} + +static void kvp_get_ipconfig_info(char *if_name, + struct hv_kvp_ipaddr_value *buffer) +{ + char cmd[512]; + char dhcp_info[128]; + char *p; + FILE *file; + + /* + * Get the address of default gateway (ipv4). + */ + sprintf(cmd, "%s %s", "ip route show dev", if_name); + strcat(cmd, " | awk '/default/ {print $3 }'"); + + /* + * Execute the command to gather gateway info. + */ + kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way, + (MAX_GATEWAY_SIZE * 2), INET_ADDRSTRLEN, 0); + + /* + * Get the address of default gateway (ipv6). + */ + sprintf(cmd, "%s %s", "ip -f inet6 route show dev", if_name); + strcat(cmd, " | awk '/default/ {print $3 }'"); + + /* + * Execute the command to gather gateway info (ipv6). + */ + kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way, + (MAX_GATEWAY_SIZE * 2), INET6_ADDRSTRLEN, 1); + + + /* + * Gather the DNS state. + * Since there is no standard way to get this information + * across various distributions of interest; we just invoke + * an external script that needs to be ported across distros + * of interest. + * + * Following is the expected format of the information from the script: + * + * ipaddr1 (nameserver1) + * ipaddr2 (nameserver2) + * . + * . + */ + + sprintf(cmd, "%s", "hv_get_dns_info"); + + /* + * Execute the command to gather DNS info. + */ + kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr, + (MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0); + + /* + * Gather the DHCP state. + * We will gather this state by invoking an external script. + * The parameter to the script is the interface name. + * Here is the expected output: + * + * Enabled: DHCP enabled. + */ + + sprintf(cmd, "%s %s", "hv_get_dhcp_info", if_name); + + file = popen(cmd, "r"); + if (file == NULL) + return; + + p = fgets(dhcp_info, sizeof(dhcp_info), file); + if (p == NULL) { + pclose(file); + return; + } + + if (!strncmp(p, "Enabled", 7)) + buffer->dhcp_enabled = 1; + else + buffer->dhcp_enabled = 0; + + pclose(file); +} + + +static unsigned int hweight32(unsigned int *w) +{ + unsigned int res = *w - ((*w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res + (res >> 4)) & 0x0F0F0F0F; + res = res + (res >> 8); + return (res + (res >> 16)) & 0x000000FF; +} + +static int kvp_process_ip_address(void *addrp, + int family, char *buffer, + int length, int *offset) +{ + struct sockaddr_in *addr; + struct sockaddr_in6 *addr6; + int addr_length; + char tmp[50]; + const char *str; + + if (family == AF_INET) { + addr = (struct sockaddr_in *)addrp; + str = inet_ntop(family, &addr->sin_addr, tmp, 50); + addr_length = INET_ADDRSTRLEN; + } else { + addr6 = (struct sockaddr_in6 *)addrp; + str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50); + addr_length = INET6_ADDRSTRLEN; + } + + if ((length - *offset) < addr_length + 2) + return HV_E_FAIL; + if (str == NULL) { + strcpy(buffer, "inet_ntop failed\n"); + return HV_E_FAIL; + } + if (*offset == 0) + strcpy(buffer, tmp); + else { + strcat(buffer, ";"); + strcat(buffer, tmp); + } + + *offset += strlen(str) + 1; + + return 0; +} + +static int +kvp_get_ip_info(int family, char *if_name, int op, + void *out_buffer, unsigned int length) +{ + struct ifaddrs *ifap; + struct ifaddrs *curp; + int offset = 0; + int sn_offset = 0; + int error = 0; + char *buffer; + struct hv_kvp_ipaddr_value *ip_buffer; + char cidr_mask[5]; /* /xyz */ + int weight; + int i; + unsigned int *w; + char *sn_str; + struct sockaddr_in6 *addr6; + + if (op == KVP_OP_ENUMERATE) { + buffer = out_buffer; + } else { + ip_buffer = out_buffer; + buffer = (char *)ip_buffer->ip_addr; + ip_buffer->addr_family = 0; + } + /* + * On entry into this function, the buffer is capable of holding the + * maximum key value. + */ + + if (getifaddrs(&ifap)) { + strcpy(buffer, "getifaddrs failed\n"); + return HV_E_FAIL; + } + + curp = ifap; + while (curp != NULL) { + if (curp->ifa_addr == NULL) { + curp = curp->ifa_next; + continue; + } + + if ((if_name != NULL) && + (strncmp(curp->ifa_name, if_name, strlen(if_name)))) { + /* + * We want info about a specific interface; + * just continue. + */ + curp = curp->ifa_next; + continue; + } + + /* + * We only support two address families: AF_INET and AF_INET6. + * If a family value of 0 is specified, we collect both + * supported address families; if not we gather info on + * the specified address family. + */ + if ((((family != 0) && + (curp->ifa_addr->sa_family != family))) || + (curp->ifa_flags & IFF_LOOPBACK)) { + curp = curp->ifa_next; + continue; + } + if ((curp->ifa_addr->sa_family != AF_INET) && + (curp->ifa_addr->sa_family != AF_INET6)) { + curp = curp->ifa_next; + continue; + } + + if (op == KVP_OP_GET_IP_INFO) { + /* + * Gather info other than the IP address. + * IP address info will be gathered later. + */ + if (curp->ifa_addr->sa_family == AF_INET) { + ip_buffer->addr_family |= ADDR_FAMILY_IPV4; + /* + * Get subnet info. + */ + error = kvp_process_ip_address( + curp->ifa_netmask, + AF_INET, + (char *) + ip_buffer->sub_net, + length, + &sn_offset); + if (error) + goto gather_ipaddr; + } else { + ip_buffer->addr_family |= ADDR_FAMILY_IPV6; + + /* + * Get subnet info in CIDR format. + */ + weight = 0; + sn_str = (char *)ip_buffer->sub_net; + addr6 = (struct sockaddr_in6 *) + curp->ifa_netmask; + w = addr6->sin6_addr.s6_addr32; + + for (i = 0; i < 4; i++) + weight += hweight32(&w[i]); + + sprintf(cidr_mask, "/%d", weight); + if (length < sn_offset + strlen(cidr_mask) + 1) + goto gather_ipaddr; + + if (sn_offset == 0) + strcpy(sn_str, cidr_mask); + else { + strcat((char *)ip_buffer->sub_net, ";"); + strcat(sn_str, cidr_mask); + } + sn_offset += strlen(sn_str) + 1; + } + + /* + * Collect other ip related configuration info. + */ + + kvp_get_ipconfig_info(if_name, ip_buffer); + } + +gather_ipaddr: + error = kvp_process_ip_address(curp->ifa_addr, + curp->ifa_addr->sa_family, + buffer, + length, &offset); + if (error) + goto getaddr_done; + + curp = curp->ifa_next; + } + +getaddr_done: + freeifaddrs(ifap); + return error; +} + + +static int expand_ipv6(char *addr, int type) +{ + int ret; + struct in6_addr v6_addr; + + ret = inet_pton(AF_INET6, addr, &v6_addr); + + if (ret != 1) { + if (type == NETMASK) + return 1; + return 0; + } + + sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:" + "%02x%02x:%02x%02x:%02x%02x", + (int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1], + (int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3], + (int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5], + (int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7], + (int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9], + (int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11], + (int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13], + (int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]); + + return 1; + +} + +static int is_ipv4(char *addr) +{ + int ret; + struct in_addr ipv4_addr; + + ret = inet_pton(AF_INET, addr, &ipv4_addr); + + if (ret == 1) + return 1; + return 0; +} + +static int parse_ip_val_buffer(char *in_buf, int *offset, + char *out_buf, int out_len) +{ + char *x; + char *start; + + /* + * in_buf has sequence of characters that are seperated by + * the character ';'. The last sequence does not have the + * terminating ";" character. + */ + start = in_buf + *offset; + + x = strchr(start, ';'); + if (x) + *x = 0; + else + x = start + strlen(start); + + if (strlen(start) != 0) { + int i = 0; + /* + * Get rid of leading spaces. + */ + while (start[i] == ' ') + i++; + + if ((x - start) <= out_len) { + strcpy(out_buf, (start + i)); + *offset += (x - start) + 1; + return 1; + } + } + return 0; +} + +static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3) +{ + int ret; + + ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3); + + if (ret < 0) + return HV_E_FAIL; + + return 0; +} + + +static int process_ip_string(FILE *f, char *ip_string, int type) +{ + int error = 0; + char addr[INET6_ADDRSTRLEN]; + int i = 0; + int j = 0; + char str[256]; + char sub_str[10]; + int offset = 0; + + memset(addr, 0, sizeof(addr)); + + while (parse_ip_val_buffer(ip_string, &offset, addr, + (MAX_IP_ADDR_SIZE * 2))) { + + sub_str[0] = 0; + if (is_ipv4(addr)) { + switch (type) { + case IPADDR: + snprintf(str, sizeof(str), "%s", "IPADDR"); + break; + case NETMASK: + snprintf(str, sizeof(str), "%s", "NETMASK"); + break; + case GATEWAY: + snprintf(str, sizeof(str), "%s", "GATEWAY"); + break; + case DNS: + snprintf(str, sizeof(str), "%s", "DNS"); + break; + } + + if (type == DNS) { + snprintf(sub_str, sizeof(sub_str), "%d", ++i); + } else if (type == GATEWAY && i == 0) { + ++i; + } else { + snprintf(sub_str, sizeof(sub_str), "%d", i++); + } + + + } else if (expand_ipv6(addr, type)) { + switch (type) { + case IPADDR: + snprintf(str, sizeof(str), "%s", "IPV6ADDR"); + break; + case NETMASK: + snprintf(str, sizeof(str), "%s", "IPV6NETMASK"); + break; + case GATEWAY: + snprintf(str, sizeof(str), "%s", + "IPV6_DEFAULTGW"); + break; + case DNS: + snprintf(str, sizeof(str), "%s", "DNS"); + break; + } + + if (type == DNS) { + snprintf(sub_str, sizeof(sub_str), "%d", ++i); + } else if (j == 0) { + ++j; + } else { + snprintf(sub_str, sizeof(sub_str), "_%d", j++); + } + } else { + return HV_INVALIDARG; + } + + error = kvp_write_file(f, str, sub_str, addr); + if (error) + return error; + memset(addr, 0, sizeof(addr)); + } + + return 0; +} + +static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) +{ + int error = 0; + char if_file[128]; + FILE *file; + char cmd[512]; + char *mac_addr; + + /* + * Set the configuration for the specified interface with + * the information provided. Since there is no standard + * way to configure an interface, we will have an external + * script that does the job of configuring the interface and + * flushing the configuration. + * + * The parameters passed to this external script are: + * 1. A configuration file that has the specified configuration. + * + * We will embed the name of the interface in the configuration + * file: ifcfg-ethx (where ethx is the interface name). + * + * The information provided here may be more than what is needed + * in a given distro to configure the interface and so are free + * ignore information that may not be relevant. + * + * Here is the format of the ip configuration file: + * + * HWADDR=macaddr + * DEVICE=interface name + * BOOTPROTO= (where is "dhcp" if DHCP is configured + * or "none" if no boot-time protocol should be used) + * + * IPADDR0=ipaddr1 + * IPADDR1=ipaddr2 + * IPADDRx=ipaddry (where y = x + 1) + * + * NETMASK0=netmask1 + * NETMASKx=netmasky (where y = x + 1) + * + * GATEWAY=ipaddr1 + * GATEWAYx=ipaddry (where y = x + 1) + * + * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) + * + * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be + * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as + * IPV6NETMASK. + * + * The host can specify multiple ipv4 and ipv6 addresses to be + * configured for the interface. Furthermore, the configuration + * needs to be persistent. A subsequent GET call on the interface + * is expected to return the configuration that is set via the SET + * call. + */ + + snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC, + "/ifcfg-", if_name); + + file = fopen(if_file, "w"); + + if (file == NULL) { + syslog(LOG_ERR, "Failed to open config file; error: %d %s", + errno, strerror(errno)); + return HV_E_FAIL; + } + + /* + * First write out the MAC address. + */ + + mac_addr = kvp_if_name_to_mac(if_name); + if (mac_addr == NULL) { + error = HV_E_FAIL; + goto setval_error; + } + + error = kvp_write_file(file, "HWADDR", "", mac_addr); + free(mac_addr); + if (error) + goto setval_error; + + error = kvp_write_file(file, "DEVICE", "", if_name); + if (error) + goto setval_error; + + /* + * The dhcp_enabled flag is only for IPv4. In the case the host only + * injects an IPv6 address, the flag is true, but we still need to + * proceed to parse and pass the IPv6 information to the + * disto-specific script hv_set_ifconfig. + */ + if (new_val->dhcp_enabled) { + error = kvp_write_file(file, "BOOTPROTO", "", "dhcp"); + if (error) + goto setval_error; + + } else { + error = kvp_write_file(file, "BOOTPROTO", "", "none"); + if (error) + goto setval_error; + } + + /* + * Write the configuration for ipaddress, netmask, gateway and + * name servers. + */ + + error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR); + if (error) + goto setval_error; + + error = process_ip_string(file, (char *)new_val->sub_net, NETMASK); + if (error) + goto setval_error; + + error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY); + if (error) + goto setval_error; + + error = process_ip_string(file, (char *)new_val->dns_addr, DNS); + if (error) + goto setval_error; + + fclose(file); + + /* + * Now that we have populated the configuration file, + * invoke the external script to do its magic. + */ + + snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file); + if (system(cmd)) { + syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s", + cmd, errno, strerror(errno)); + return HV_E_FAIL; + } + return 0; + +setval_error: + syslog(LOG_ERR, "Failed to write config file"); + fclose(file); + return error; +} + + +static void +kvp_get_domain_name(char *buffer, int length) +{ + struct addrinfo hints, *info ; + int error = 0; + + gethostname(buffer, length); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */ + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + + error = getaddrinfo(buffer, NULL, &hints, &info); + if (error != 0) { + snprintf(buffer, length, "getaddrinfo failed: 0x%x %s", + error, gai_strerror(error)); + return; + } + snprintf(buffer, length, "%s", info->ai_canonname); + freeaddrinfo(info); +} + +static int +netlink_send(int fd, struct cn_msg *msg) +{ + struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE }; + unsigned int size; + struct msghdr message; + struct iovec iov[2]; + + size = sizeof(struct cn_msg) + msg->len; + + nlh.nlmsg_pid = getpid(); + nlh.nlmsg_len = NLMSG_LENGTH(size); + + iov[0].iov_base = &nlh; + iov[0].iov_len = sizeof(nlh); + + iov[1].iov_base = msg; + iov[1].iov_len = size; + + memset(&message, 0, sizeof(message)); + message.msg_name = &addr; + message.msg_namelen = sizeof(addr); + message.msg_iov = iov; + message.msg_iovlen = 2; + + return sendmsg(fd, &message, 0); +} + +void print_usage(char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n" + "Options are:\n" + " -n, --no-daemon stay in foreground, don't daemonize\n" + " -h, --help print this help\n", argv[0]); +} + +int main(int argc, char *argv[]) +{ + int fd, len, nl_group; + int error; + struct cn_msg *message; + struct pollfd pfd; + struct nlmsghdr *incoming_msg; + struct cn_msg *incoming_cn_msg; + struct hv_kvp_msg *hv_msg; + char *p; + char *key_value; + char *key_name; + int op; + int pool; + char *if_name; + struct hv_kvp_ipaddr_value *kvp_ip_val; + char *kvp_recv_buffer; + size_t kvp_recv_buffer_len; + int daemonize = 1, long_index = 0, opt; + + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"no-daemon", no_argument, 0, 'n' }, + {0, 0, 0, 0 } + }; + + while ((opt = getopt_long(argc, argv, "hn", long_options, + &long_index)) != -1) { + switch (opt) { + case 'n': + daemonize = 0; + break; + case 'h': + default: + print_usage(argv); + exit(EXIT_FAILURE); + } + } + + if (daemonize && daemon(1, 0)) + return 1; + + openlog("KVP", 0, LOG_USER); + syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); + + kvp_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg); + kvp_recv_buffer = calloc(1, kvp_recv_buffer_len); + if (!kvp_recv_buffer) { + syslog(LOG_ERR, "Failed to allocate netlink buffer"); + exit(EXIT_FAILURE); + } + /* + * Retrieve OS release information. + */ + kvp_get_os_info(); + /* + * Cache Fully Qualified Domain Name because getaddrinfo takes an + * unpredictable amount of time to finish. + */ + kvp_get_domain_name(full_domain_name, sizeof(full_domain_name)); + + if (kvp_file_init()) { + syslog(LOG_ERR, "Failed to initialize the pools"); + exit(EXIT_FAILURE); + } + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (fd < 0) { + syslog(LOG_ERR, "netlink socket creation failed; error: %d %s", errno, + strerror(errno)); + exit(EXIT_FAILURE); + } + addr.nl_family = AF_NETLINK; + addr.nl_pad = 0; + addr.nl_pid = 0; + addr.nl_groups = 0; + + + error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (error < 0) { + syslog(LOG_ERR, "bind failed; error: %d %s", errno, strerror(errno)); + close(fd); + exit(EXIT_FAILURE); + } + nl_group = CN_KVP_IDX; + + if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)) < 0) { + syslog(LOG_ERR, "setsockopt failed; error: %d %s", errno, strerror(errno)); + close(fd); + exit(EXIT_FAILURE); + } + + /* + * Register ourselves with the kernel. + */ + message = (struct cn_msg *)kvp_recv_buffer; + message->id.idx = CN_KVP_IDX; + message->id.val = CN_KVP_VAL; + + hv_msg = (struct hv_kvp_msg *)message->data; + hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1; + message->ack = 0; + message->len = sizeof(struct hv_kvp_msg); + + len = netlink_send(fd, message); + if (len < 0) { + syslog(LOG_ERR, "netlink_send failed; error: %d %s", errno, strerror(errno)); + close(fd); + exit(EXIT_FAILURE); + } + + pfd.fd = fd; + + while (1) { + struct sockaddr *addr_p = (struct sockaddr *) &addr; + socklen_t addr_l = sizeof(addr); + pfd.events = POLLIN; + pfd.revents = 0; + + if (poll(&pfd, 1, -1) < 0) { + syslog(LOG_ERR, "poll failed; error: %d %s", errno, strerror(errno)); + if (errno == EINVAL) { + close(fd); + exit(EXIT_FAILURE); + } + else + continue; + } + + len = recvfrom(fd, kvp_recv_buffer, kvp_recv_buffer_len, 0, + addr_p, &addr_l); + + if (len < 0) { + int saved_errno = errno; + syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", + addr.nl_pid, errno, strerror(errno)); + + if (saved_errno == ENOBUFS) { + syslog(LOG_ERR, "receive error: ignored"); + continue; + } + + close(fd); + return -1; + } + + if (addr.nl_pid) { + syslog(LOG_WARNING, "Received packet from untrusted pid:%u", + addr.nl_pid); + continue; + } + + incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; + + if (incoming_msg->nlmsg_type != NLMSG_DONE) + continue; + + incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); + hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; + + /* + * We will use the KVP header information to pass back + * the error from this daemon. So, first copy the state + * and set the error code to success. + */ + op = hv_msg->kvp_hdr.operation; + pool = hv_msg->kvp_hdr.pool; + hv_msg->error = HV_S_OK; + + if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) { + /* + * Driver is registering with us; stash away the version + * information. + */ + in_hand_shake = 0; + p = (char *)hv_msg->body.kvp_register.version; + lic_version = malloc(strlen(p) + 1); + if (lic_version) { + strcpy(lic_version, p); + syslog(LOG_INFO, "KVP LIC Version: %s", + lic_version); + } else { + syslog(LOG_ERR, "malloc failed"); + } + continue; + } + + switch (op) { + case KVP_OP_GET_IP_INFO: + kvp_ip_val = &hv_msg->body.kvp_ip_val; + if_name = + kvp_mac_to_if_name((char *)kvp_ip_val->adapter_id); + + if (if_name == NULL) { + /* + * We could not map the mac address to an + * interface name; return error. + */ + hv_msg->error = HV_E_FAIL; + break; + } + error = kvp_get_ip_info( + 0, if_name, KVP_OP_GET_IP_INFO, + kvp_ip_val, + (MAX_IP_ADDR_SIZE * 2)); + + if (error) + hv_msg->error = error; + + free(if_name); + break; + + case KVP_OP_SET_IP_INFO: + kvp_ip_val = &hv_msg->body.kvp_ip_val; + if_name = kvp_get_if_name( + (char *)kvp_ip_val->adapter_id); + if (if_name == NULL) { + /* + * We could not map the guid to an + * interface name; return error. + */ + hv_msg->error = HV_GUID_NOTFOUND; + break; + } + error = kvp_set_ip_info(if_name, kvp_ip_val); + if (error) + hv_msg->error = error; + + free(if_name); + break; + + case KVP_OP_SET: + if (kvp_key_add_or_modify(pool, + hv_msg->body.kvp_set.data.key, + hv_msg->body.kvp_set.data.key_size, + hv_msg->body.kvp_set.data.value, + hv_msg->body.kvp_set.data.value_size)) + hv_msg->error = HV_S_CONT; + break; + + case KVP_OP_GET: + if (kvp_get_value(pool, + hv_msg->body.kvp_set.data.key, + hv_msg->body.kvp_set.data.key_size, + hv_msg->body.kvp_set.data.value, + hv_msg->body.kvp_set.data.value_size)) + hv_msg->error = HV_S_CONT; + break; + + case KVP_OP_DELETE: + if (kvp_key_delete(pool, + hv_msg->body.kvp_delete.key, + hv_msg->body.kvp_delete.key_size)) + hv_msg->error = HV_S_CONT; + break; + + default: + break; + } + + if (op != KVP_OP_ENUMERATE) + goto kvp_done; + + /* + * If the pool is KVP_POOL_AUTO, dynamically generate + * both the key and the value; if not read from the + * appropriate pool. + */ + if (pool != KVP_POOL_AUTO) { + if (kvp_pool_enumerate(pool, + hv_msg->body.kvp_enum_data.index, + hv_msg->body.kvp_enum_data.data.key, + HV_KVP_EXCHANGE_MAX_KEY_SIZE, + hv_msg->body.kvp_enum_data.data.value, + HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) + hv_msg->error = HV_S_CONT; + goto kvp_done; + } + + hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; + key_name = (char *)hv_msg->body.kvp_enum_data.data.key; + key_value = (char *)hv_msg->body.kvp_enum_data.data.value; + + switch (hv_msg->body.kvp_enum_data.index) { + case FullyQualifiedDomainName: + strcpy(key_value, full_domain_name); + strcpy(key_name, "FullyQualifiedDomainName"); + break; + case IntegrationServicesVersion: + strcpy(key_name, "IntegrationServicesVersion"); + strcpy(key_value, lic_version); + break; + case NetworkAddressIPv4: + kvp_get_ip_info(AF_INET, NULL, KVP_OP_ENUMERATE, + key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); + strcpy(key_name, "NetworkAddressIPv4"); + break; + case NetworkAddressIPv6: + kvp_get_ip_info(AF_INET6, NULL, KVP_OP_ENUMERATE, + key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); + strcpy(key_name, "NetworkAddressIPv6"); + break; + case OSBuildNumber: + strcpy(key_value, os_build); + strcpy(key_name, "OSBuildNumber"); + break; + case OSName: + strcpy(key_value, os_name); + strcpy(key_name, "OSName"); + break; + case OSMajorVersion: + strcpy(key_value, os_major); + strcpy(key_name, "OSMajorVersion"); + break; + case OSMinorVersion: + strcpy(key_value, os_minor); + strcpy(key_name, "OSMinorVersion"); + break; + case OSVersion: + strcpy(key_value, os_version); + strcpy(key_name, "OSVersion"); + break; + case ProcessorArchitecture: + strcpy(key_value, processor_arch); + strcpy(key_name, "ProcessorArchitecture"); + break; + default: + hv_msg->error = HV_S_CONT; + break; + } + /* + * Send the value back to the kernel. The response is + * already in the receive buffer. Update the cn_msg header to + * reflect the key value that has been added to the message + */ +kvp_done: + + incoming_cn_msg->id.idx = CN_KVP_IDX; + incoming_cn_msg->id.val = CN_KVP_VAL; + incoming_cn_msg->ack = 0; + incoming_cn_msg->len = sizeof(struct hv_kvp_msg); + + len = netlink_send(fd, incoming_cn_msg); + if (len < 0) { + int saved_errno = errno; + syslog(LOG_ERR, "net_link send failed; error: %d %s", errno, + strerror(errno)); + + if (saved_errno == ENOMEM || saved_errno == ENOBUFS) { + syslog(LOG_ERR, "send error: ignored"); + continue; + } + + exit(EXIT_FAILURE); + } + } + +} diff --git a/kernel/tools/hv/hv_set_ifconfig.sh b/kernel/tools/hv/hv_set_ifconfig.sh new file mode 100755 index 000000000..735aafd64 --- /dev/null +++ b/kernel/tools/hv/hv_set_ifconfig.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +# This example script activates an interface based on the specified +# configuration. +# +# In the interest of keeping the KVP daemon code free of distro specific +# information; the kvp daemon code invokes this external script to configure +# the interface. +# +# The only argument to this script is the configuration file that is to +# be used to configure the interface. +# +# Each Distro is expected to implement this script in a distro specific +# fashion. For instance on Distros that ship with Network Manager enabled, +# this script can be based on the Network Manager APIs for configuring the +# interface. +# +# This example script is based on a RHEL environment. +# +# Here is the format of the ip configuration file: +# +# HWADDR=macaddr +# DEVICE=interface name +# BOOTPROTO= (where is "dhcp" if DHCP is configured +# or "none" if no boot-time protocol should be used) +# +# IPADDR0=ipaddr1 +# IPADDR1=ipaddr2 +# IPADDRx=ipaddry (where y = x + 1) +# +# NETMASK0=netmask1 +# NETMASKx=netmasky (where y = x + 1) +# +# GATEWAY=ipaddr1 +# GATEWAYx=ipaddry (where y = x + 1) +# +# DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) +# +# IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be +# tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as +# IPV6NETMASK. +# +# The host can specify multiple ipv4 and ipv6 addresses to be +# configured for the interface. Furthermore, the configuration +# needs to be persistent. A subsequent GET call on the interface +# is expected to return the configuration that is set via the SET +# call. +# + + + +echo "IPV6INIT=yes" >> $1 +echo "NM_CONTROLLED=no" >> $1 +echo "PEERDNS=yes" >> $1 +echo "ONBOOT=yes" >> $1 + + +cp $1 /etc/sysconfig/network-scripts/ + + +interface=$(echo $1 | awk -F - '{ print $2 }') + +/sbin/ifdown $interface 2>/dev/null +/sbin/ifup $interface 2>/dev/null diff --git a/kernel/tools/hv/hv_vss_daemon.c b/kernel/tools/hv/hv_vss_daemon.c new file mode 100644 index 000000000..506dd0148 --- /dev/null +++ b/kernel/tools/hv/hv_vss_daemon.c @@ -0,0 +1,342 @@ +/* + * An implementation of the host initiated guest snapshot for Hyper-V. + * + * + * Copyright (C) 2013, Microsoft, Inc. + * Author : K. Y. Srinivasan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct sockaddr_nl addr; + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + + +/* Don't use syslog() in the function since that can cause write to disk */ +static int vss_do_freeze(char *dir, unsigned int cmd) +{ + int ret, fd = open(dir, O_RDONLY); + + if (fd < 0) + return 1; + + ret = ioctl(fd, cmd, 0); + + /* + * If a partition is mounted more than once, only the first + * FREEZE/THAW can succeed and the later ones will get + * EBUSY/EINVAL respectively: there could be 2 cases: + * 1) a user may mount the same partition to differnt directories + * by mistake or on purpose; + * 2) The subvolume of btrfs appears to have the same partition + * mounted more than once. + */ + if (ret) { + if ((cmd == FIFREEZE && errno == EBUSY) || + (cmd == FITHAW && errno == EINVAL)) { + close(fd); + return 0; + } + } + + close(fd); + return !!ret; +} + +static int vss_operate(int operation) +{ + char match[] = "/dev/"; + FILE *mounts; + struct mntent *ent; + char errdir[1024] = {0}; + unsigned int cmd; + int error = 0, root_seen = 0, save_errno = 0; + + switch (operation) { + case VSS_OP_FREEZE: + cmd = FIFREEZE; + break; + case VSS_OP_THAW: + cmd = FITHAW; + break; + default: + return -1; + } + + mounts = setmntent("/proc/mounts", "r"); + if (mounts == NULL) + return -1; + + while ((ent = getmntent(mounts))) { + if (strncmp(ent->mnt_fsname, match, strlen(match))) + continue; + if (hasmntopt(ent, MNTOPT_RO) != NULL) + continue; + if (strcmp(ent->mnt_type, "vfat") == 0) + continue; + if (strcmp(ent->mnt_dir, "/") == 0) { + root_seen = 1; + continue; + } + error |= vss_do_freeze(ent->mnt_dir, cmd); + if (error && operation == VSS_OP_FREEZE) + goto err; + } + + endmntent(mounts); + + if (root_seen) { + error |= vss_do_freeze("/", cmd); + if (error && operation == VSS_OP_FREEZE) + goto err; + } + + goto out; +err: + save_errno = errno; + if (ent) { + strncpy(errdir, ent->mnt_dir, sizeof(errdir)-1); + endmntent(mounts); + } + vss_operate(VSS_OP_THAW); + /* Call syslog after we thaw all filesystems */ + if (ent) + syslog(LOG_ERR, "FREEZE of %s failed; error:%d %s", + errdir, save_errno, strerror(save_errno)); + else + syslog(LOG_ERR, "FREEZE of / failed; error:%d %s", save_errno, + strerror(save_errno)); +out: + return error; +} + +static int netlink_send(int fd, struct cn_msg *msg) +{ + struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE }; + unsigned int size; + struct msghdr message; + struct iovec iov[2]; + + size = sizeof(struct cn_msg) + msg->len; + + nlh.nlmsg_pid = getpid(); + nlh.nlmsg_len = NLMSG_LENGTH(size); + + iov[0].iov_base = &nlh; + iov[0].iov_len = sizeof(nlh); + + iov[1].iov_base = msg; + iov[1].iov_len = size; + + memset(&message, 0, sizeof(message)); + message.msg_name = &addr; + message.msg_namelen = sizeof(addr); + message.msg_iov = iov; + message.msg_iovlen = 2; + + return sendmsg(fd, &message, 0); +} + +void print_usage(char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n" + "Options are:\n" + " -n, --no-daemon stay in foreground, don't daemonize\n" + " -h, --help print this help\n", argv[0]); +} + +int main(int argc, char *argv[]) +{ + int fd, len, nl_group; + int error; + struct cn_msg *message; + struct pollfd pfd; + struct nlmsghdr *incoming_msg; + struct cn_msg *incoming_cn_msg; + int op; + struct hv_vss_msg *vss_msg; + char *vss_recv_buffer; + size_t vss_recv_buffer_len; + int daemonize = 1, long_index = 0, opt; + + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"no-daemon", no_argument, 0, 'n' }, + {0, 0, 0, 0 } + }; + + while ((opt = getopt_long(argc, argv, "hn", long_options, + &long_index)) != -1) { + switch (opt) { + case 'n': + daemonize = 0; + break; + case 'h': + default: + print_usage(argv); + exit(EXIT_FAILURE); + } + } + + if (daemonize && daemon(1, 0)) + return 1; + + openlog("Hyper-V VSS", 0, LOG_USER); + syslog(LOG_INFO, "VSS starting; pid is:%d", getpid()); + + vss_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg); + vss_recv_buffer = calloc(1, vss_recv_buffer_len); + if (!vss_recv_buffer) { + syslog(LOG_ERR, "Failed to allocate netlink buffers"); + exit(EXIT_FAILURE); + } + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (fd < 0) { + syslog(LOG_ERR, "netlink socket creation failed; error:%d %s", + errno, strerror(errno)); + exit(EXIT_FAILURE); + } + addr.nl_family = AF_NETLINK; + addr.nl_pad = 0; + addr.nl_pid = 0; + addr.nl_groups = 0; + + + error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (error < 0) { + syslog(LOG_ERR, "bind failed; error:%d %s", errno, strerror(errno)); + close(fd); + exit(EXIT_FAILURE); + } + nl_group = CN_VSS_IDX; + if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)) < 0) { + syslog(LOG_ERR, "setsockopt failed; error:%d %s", errno, strerror(errno)); + close(fd); + exit(EXIT_FAILURE); + } + /* + * Register ourselves with the kernel. + */ + message = (struct cn_msg *)vss_recv_buffer; + message->id.idx = CN_VSS_IDX; + message->id.val = CN_VSS_VAL; + message->ack = 0; + vss_msg = (struct hv_vss_msg *)message->data; + vss_msg->vss_hdr.operation = VSS_OP_REGISTER; + + message->len = sizeof(struct hv_vss_msg); + + len = netlink_send(fd, message); + if (len < 0) { + syslog(LOG_ERR, "netlink_send failed; error:%d %s", errno, strerror(errno)); + close(fd); + exit(EXIT_FAILURE); + } + + pfd.fd = fd; + + while (1) { + struct sockaddr *addr_p = (struct sockaddr *) &addr; + socklen_t addr_l = sizeof(addr); + pfd.events = POLLIN; + pfd.revents = 0; + + if (poll(&pfd, 1, -1) < 0) { + syslog(LOG_ERR, "poll failed; error:%d %s", errno, strerror(errno)); + if (errno == EINVAL) { + close(fd); + exit(EXIT_FAILURE); + } + else + continue; + } + + len = recvfrom(fd, vss_recv_buffer, vss_recv_buffer_len, 0, + addr_p, &addr_l); + + if (len < 0) { + syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", + addr.nl_pid, errno, strerror(errno)); + close(fd); + return -1; + } + + if (addr.nl_pid) { + syslog(LOG_WARNING, + "Received packet from untrusted pid:%u", + addr.nl_pid); + continue; + } + + incoming_msg = (struct nlmsghdr *)vss_recv_buffer; + + if (incoming_msg->nlmsg_type != NLMSG_DONE) + continue; + + incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); + vss_msg = (struct hv_vss_msg *)incoming_cn_msg->data; + op = vss_msg->vss_hdr.operation; + error = HV_S_OK; + + switch (op) { + case VSS_OP_FREEZE: + case VSS_OP_THAW: + error = vss_operate(op); + syslog(LOG_INFO, "VSS: op=%s: %s\n", + op == VSS_OP_FREEZE ? "FREEZE" : "THAW", + error ? "failed" : "succeeded"); + + if (error) { + error = HV_E_FAIL; + syslog(LOG_ERR, "op=%d failed!", op); + syslog(LOG_ERR, "report it with these files:"); + syslog(LOG_ERR, "/etc/fstab and /proc/mounts"); + } + break; + default: + syslog(LOG_ERR, "Illegal op:%d\n", op); + } + vss_msg->error = error; + len = netlink_send(fd, incoming_cn_msg); + if (len < 0) { + syslog(LOG_ERR, "net_link send failed; error:%d %s", + errno, strerror(errno)); + exit(EXIT_FAILURE); + } + } + +} diff --git a/kernel/tools/iio/Makefile b/kernel/tools/iio/Makefile new file mode 100644 index 000000000..bf7ae6d66 --- /dev/null +++ b/kernel/tools/iio/Makefile @@ -0,0 +1,16 @@ +CC = gcc +CFLAGS = -Wall -g -D_GNU_SOURCE + +all: iio_event_monitor lsiio generic_buffer + +iio_event_monitor: iio_event_monitor.o iio_utils.o + +lsiio: lsiio.o iio_utils.o + +generic_buffer: generic_buffer.o iio_utils.o + +%.o: %.c iio_utils.h + +.PHONY: clean +clean: + rm -f *.o iio_event_monitor lsiio generic_buffer diff --git a/kernel/tools/iio/generic_buffer.c b/kernel/tools/iio/generic_buffer.c new file mode 100644 index 000000000..f805493be --- /dev/null +++ b/kernel/tools/iio/generic_buffer.c @@ -0,0 +1,359 @@ +/* Industrialio buffer test code. + * + * Copyright (c) 2008 Jonathan Cameron + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is primarily intended as an example application. + * Reads the current buffer setup from sysfs and starts a short capture + * from the specified device, pretty printing the result after appropriate + * conversion. + * + * Command line parameters + * generic_buffer -n -t + * If trigger name is not specified the program assumes you want a dataready + * trigger associated with the device and goes looking for it. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "iio_utils.h" + +/** + * size_from_channelarray() - calculate the storage size of a scan + * @channels: the channel info array + * @num_channels: number of channels + * + * Has the side effect of filling the channels[i].location values used + * in processing the buffer output. + **/ +int size_from_channelarray(struct iio_channel_info *channels, int num_channels) +{ + int bytes = 0; + int i = 0; + + while (i < num_channels) { + if (bytes % channels[i].bytes == 0) + channels[i].location = bytes; + else + channels[i].location = bytes - bytes%channels[i].bytes + + channels[i].bytes; + bytes = channels[i].location + channels[i].bytes; + i++; + } + return bytes; +} + +void print2byte(int input, struct iio_channel_info *info) +{ + /* First swap if incorrect endian */ + if (info->be) + input = be16toh((uint16_t)input); + else + input = le16toh((uint16_t)input); + + /* + * Shift before conversion to avoid sign extension + * of left aligned data + */ + input >>= info->shift; + if (info->is_signed) { + int16_t val = input; + + val &= (1 << info->bits_used) - 1; + val = (int16_t)(val << (16 - info->bits_used)) >> + (16 - info->bits_used); + printf("%05f ", ((float)val + info->offset)*info->scale); + } else { + uint16_t val = input; + + val &= (1 << info->bits_used) - 1; + printf("%05f ", ((float)val + info->offset)*info->scale); + } +} +/** + * process_scan() - print out the values in SI units + * @data: pointer to the start of the scan + * @channels: information about the channels. Note + * size_from_channelarray must have been called first to fill the + * location offsets. + * @num_channels: number of channels + **/ +void process_scan(char *data, + struct iio_channel_info *channels, + int num_channels) +{ + int k; + + for (k = 0; k < num_channels; k++) + switch (channels[k].bytes) { + /* only a few cases implemented so far */ + case 2: + print2byte(*(uint16_t *)(data + channels[k].location), + &channels[k]); + break; + case 4: + if (!channels[k].is_signed) { + uint32_t val = *(uint32_t *) + (data + channels[k].location); + printf("%05f ", ((float)val + + channels[k].offset)* + channels[k].scale); + + } + break; + case 8: + if (channels[k].is_signed) { + int64_t val = *(int64_t *) + (data + + channels[k].location); + if ((val >> channels[k].bits_used) & 1) + val = (val & channels[k].mask) | + ~channels[k].mask; + /* special case for timestamp */ + if (channels[k].scale == 1.0f && + channels[k].offset == 0.0f) + printf("%" PRId64 " ", val); + else + printf("%05f ", ((float)val + + channels[k].offset)* + channels[k].scale); + } + break; + default: + break; + } + printf("\n"); +} + +int main(int argc, char **argv) +{ + unsigned long num_loops = 2; + unsigned long timedelay = 1000000; + unsigned long buf_len = 128; + + int ret, c, i, j, toread; + int fp; + + int num_channels; + char *trigger_name = NULL, *device_name = NULL; + char *dev_dir_name, *buf_dir_name; + + int datardytrigger = 1; + char *data; + ssize_t read_size; + int dev_num, trig_num; + char *buffer_access; + int scan_size; + int noevents = 0; + int notrigger = 0; + char *dummy; + + struct iio_channel_info *channels; + + while ((c = getopt(argc, argv, "l:w:c:et:n:g")) != -1) { + switch (c) { + case 'n': + device_name = optarg; + break; + case 't': + trigger_name = optarg; + datardytrigger = 0; + break; + case 'e': + noevents = 1; + break; + case 'c': + num_loops = strtoul(optarg, &dummy, 10); + break; + case 'w': + timedelay = strtoul(optarg, &dummy, 10); + break; + case 'l': + buf_len = strtoul(optarg, &dummy, 10); + break; + case 'g': + notrigger = 1; + break; + case '?': + return -1; + } + } + + if (device_name == NULL) + return -1; + + /* Find the device requested */ + dev_num = find_type_by_name(device_name, "iio:device"); + if (dev_num < 0) { + printf("Failed to find the %s\n", device_name); + ret = -ENODEV; + goto error_ret; + } + printf("iio device number being used is %d\n", dev_num); + + asprintf(&dev_dir_name, "%siio:device%d", iio_dir, dev_num); + + if (!notrigger) { + if (trigger_name == NULL) { + /* + * Build the trigger name. If it is device associated + * its name is _dev[n] where n matches + * the device number found above. + */ + ret = asprintf(&trigger_name, + "%s-dev%d", device_name, dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error_ret; + } + } + + /* Verify the trigger exists */ + trig_num = find_type_by_name(trigger_name, "trigger"); + if (trig_num < 0) { + printf("Failed to find the trigger %s\n", trigger_name); + ret = -ENODEV; + goto error_free_triggername; + } + printf("iio trigger number being used is %d\n", trig_num); + } else + printf("trigger-less mode selected\n"); + + /* + * Parse the files in scan_elements to identify what channels are + * present + */ + ret = build_channel_array(dev_dir_name, &channels, &num_channels); + if (ret) { + printf("Problem reading scan element information\n"); + printf("diag %s\n", dev_dir_name); + goto error_free_triggername; + } + + /* + * Construct the directory name for the associated buffer. + * As we know that the lis3l02dq has only one buffer this may + * be built rather than found. + */ + ret = asprintf(&buf_dir_name, + "%siio:device%d/buffer", iio_dir, dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_triggername; + } + + if (!notrigger) { + printf("%s %s\n", dev_dir_name, trigger_name); + /* Set the device trigger to be the data ready trigger found + * above */ + ret = write_sysfs_string_and_verify("trigger/current_trigger", + dev_dir_name, + trigger_name); + if (ret < 0) { + printf("Failed to write current_trigger file\n"); + goto error_free_buf_dir_name; + } + } + + /* Setup ring buffer parameters */ + ret = write_sysfs_int("length", buf_dir_name, buf_len); + if (ret < 0) + goto error_free_buf_dir_name; + + /* Enable the buffer */ + ret = write_sysfs_int("enable", buf_dir_name, 1); + if (ret < 0) + goto error_free_buf_dir_name; + scan_size = size_from_channelarray(channels, num_channels); + data = malloc(scan_size*buf_len); + if (!data) { + ret = -ENOMEM; + goto error_free_buf_dir_name; + } + + ret = asprintf(&buffer_access, "/dev/iio:device%d", dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_data; + } + + /* Attempt to open non blocking the access dev */ + fp = open(buffer_access, O_RDONLY | O_NONBLOCK); + if (fp == -1) { /* If it isn't there make the node */ + printf("Failed to open %s\n", buffer_access); + ret = -errno; + goto error_free_buffer_access; + } + + /* Wait for events 10 times */ + for (j = 0; j < num_loops; j++) { + if (!noevents) { + struct pollfd pfd = { + .fd = fp, + .events = POLLIN, + }; + + poll(&pfd, 1, -1); + toread = buf_len; + + } else { + usleep(timedelay); + toread = 64; + } + + read_size = read(fp, + data, + toread*scan_size); + if (read_size < 0) { + if (errno == -EAGAIN) { + printf("nothing available\n"); + continue; + } else + break; + } + for (i = 0; i < read_size/scan_size; i++) + process_scan(data + scan_size*i, + channels, + num_channels); + } + + /* Stop the buffer */ + ret = write_sysfs_int("enable", buf_dir_name, 0); + if (ret < 0) + goto error_close_buffer_access; + + if (!notrigger) + /* Disconnect the trigger - just write a dummy name. */ + write_sysfs_string("trigger/current_trigger", + dev_dir_name, "NULL"); + +error_close_buffer_access: + close(fp); +error_free_data: + free(data); +error_free_buffer_access: + free(buffer_access); +error_free_buf_dir_name: + free(buf_dir_name); +error_free_triggername: + if (datardytrigger) + free(trigger_name); +error_ret: + return ret; +} diff --git a/kernel/tools/iio/iio_event_monitor.c b/kernel/tools/iio/iio_event_monitor.c new file mode 100644 index 000000000..427c271ac --- /dev/null +++ b/kernel/tools/iio/iio_event_monitor.c @@ -0,0 +1,308 @@ +/* Industrialio event test code. + * + * Copyright (c) 2011-2012 Lars-Peter Clausen + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is primarily intended as an example application. + * Reads the current buffer setup from sysfs and starts a short capture + * from the specified device, pretty printing the result after appropriate + * conversion. + * + * Usage: + * iio_event_monitor + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "iio_utils.h" +#include +#include + +static const char * const iio_chan_type_name_spec[] = { + [IIO_VOLTAGE] = "voltage", + [IIO_CURRENT] = "current", + [IIO_POWER] = "power", + [IIO_ACCEL] = "accel", + [IIO_ANGL_VEL] = "anglvel", + [IIO_MAGN] = "magn", + [IIO_LIGHT] = "illuminance", + [IIO_INTENSITY] = "intensity", + [IIO_PROXIMITY] = "proximity", + [IIO_TEMP] = "temp", + [IIO_INCLI] = "incli", + [IIO_ROT] = "rot", + [IIO_ANGL] = "angl", + [IIO_TIMESTAMP] = "timestamp", + [IIO_CAPACITANCE] = "capacitance", + [IIO_ALTVOLTAGE] = "altvoltage", + [IIO_CCT] = "cct", + [IIO_PRESSURE] = "pressure", + [IIO_HUMIDITYRELATIVE] = "humidityrelative", + [IIO_ACTIVITY] = "activity", + [IIO_STEPS] = "steps", +}; + +static const char * const iio_ev_type_text[] = { + [IIO_EV_TYPE_THRESH] = "thresh", + [IIO_EV_TYPE_MAG] = "mag", + [IIO_EV_TYPE_ROC] = "roc", + [IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive", + [IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive", + [IIO_EV_TYPE_CHANGE] = "change", +}; + +static const char * const iio_ev_dir_text[] = { + [IIO_EV_DIR_EITHER] = "either", + [IIO_EV_DIR_RISING] = "rising", + [IIO_EV_DIR_FALLING] = "falling" +}; + +static const char * const iio_modifier_names[] = { + [IIO_MOD_X] = "x", + [IIO_MOD_Y] = "y", + [IIO_MOD_Z] = "z", + [IIO_MOD_X_AND_Y] = "x&y", + [IIO_MOD_X_AND_Z] = "x&z", + [IIO_MOD_Y_AND_Z] = "y&z", + [IIO_MOD_X_AND_Y_AND_Z] = "x&y&z", + [IIO_MOD_X_OR_Y] = "x|y", + [IIO_MOD_X_OR_Z] = "x|z", + [IIO_MOD_Y_OR_Z] = "y|z", + [IIO_MOD_X_OR_Y_OR_Z] = "x|y|z", + [IIO_MOD_LIGHT_BOTH] = "both", + [IIO_MOD_LIGHT_IR] = "ir", + [IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)", + [IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2", + [IIO_MOD_LIGHT_CLEAR] = "clear", + [IIO_MOD_LIGHT_RED] = "red", + [IIO_MOD_LIGHT_GREEN] = "green", + [IIO_MOD_LIGHT_BLUE] = "blue", + [IIO_MOD_QUATERNION] = "quaternion", + [IIO_MOD_TEMP_AMBIENT] = "ambient", + [IIO_MOD_TEMP_OBJECT] = "object", + [IIO_MOD_NORTH_MAGN] = "from_north_magnetic", + [IIO_MOD_NORTH_TRUE] = "from_north_true", + [IIO_MOD_NORTH_MAGN_TILT_COMP] = "from_north_magnetic_tilt_comp", + [IIO_MOD_NORTH_TRUE_TILT_COMP] = "from_north_true_tilt_comp", + [IIO_MOD_RUNNING] = "running", + [IIO_MOD_JOGGING] = "jogging", + [IIO_MOD_WALKING] = "walking", + [IIO_MOD_STILL] = "still", +}; + +static bool event_is_known(struct iio_event_data *event) +{ + enum iio_chan_type type = IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event->id); + enum iio_modifier mod = IIO_EVENT_CODE_EXTRACT_MODIFIER(event->id); + enum iio_event_type ev_type = IIO_EVENT_CODE_EXTRACT_TYPE(event->id); + enum iio_event_direction dir = IIO_EVENT_CODE_EXTRACT_DIR(event->id); + + switch (type) { + case IIO_VOLTAGE: + case IIO_CURRENT: + case IIO_POWER: + case IIO_ACCEL: + case IIO_ANGL_VEL: + case IIO_MAGN: + case IIO_LIGHT: + case IIO_INTENSITY: + case IIO_PROXIMITY: + case IIO_TEMP: + case IIO_INCLI: + case IIO_ROT: + case IIO_ANGL: + case IIO_TIMESTAMP: + case IIO_CAPACITANCE: + case IIO_ALTVOLTAGE: + case IIO_CCT: + case IIO_PRESSURE: + case IIO_HUMIDITYRELATIVE: + case IIO_ACTIVITY: + case IIO_STEPS: + break; + default: + return false; + } + + switch (mod) { + case IIO_NO_MOD: + case IIO_MOD_X: + case IIO_MOD_Y: + case IIO_MOD_Z: + case IIO_MOD_X_AND_Y: + case IIO_MOD_X_AND_Z: + case IIO_MOD_Y_AND_Z: + case IIO_MOD_X_AND_Y_AND_Z: + case IIO_MOD_X_OR_Y: + case IIO_MOD_X_OR_Z: + case IIO_MOD_Y_OR_Z: + case IIO_MOD_X_OR_Y_OR_Z: + case IIO_MOD_LIGHT_BOTH: + case IIO_MOD_LIGHT_IR: + case IIO_MOD_ROOT_SUM_SQUARED_X_Y: + case IIO_MOD_SUM_SQUARED_X_Y_Z: + case IIO_MOD_LIGHT_CLEAR: + case IIO_MOD_LIGHT_RED: + case IIO_MOD_LIGHT_GREEN: + case IIO_MOD_LIGHT_BLUE: + case IIO_MOD_QUATERNION: + case IIO_MOD_TEMP_AMBIENT: + case IIO_MOD_TEMP_OBJECT: + case IIO_MOD_NORTH_MAGN: + case IIO_MOD_NORTH_TRUE: + case IIO_MOD_NORTH_MAGN_TILT_COMP: + case IIO_MOD_NORTH_TRUE_TILT_COMP: + case IIO_MOD_RUNNING: + case IIO_MOD_JOGGING: + case IIO_MOD_WALKING: + case IIO_MOD_STILL: + break; + default: + return false; + } + + switch (ev_type) { + case IIO_EV_TYPE_THRESH: + case IIO_EV_TYPE_MAG: + case IIO_EV_TYPE_ROC: + case IIO_EV_TYPE_THRESH_ADAPTIVE: + case IIO_EV_TYPE_MAG_ADAPTIVE: + case IIO_EV_TYPE_CHANGE: + break; + default: + return false; + } + + switch (dir) { + case IIO_EV_DIR_EITHER: + case IIO_EV_DIR_RISING: + case IIO_EV_DIR_FALLING: + case IIO_EV_DIR_NONE: + break; + default: + return false; + } + + return true; +} + +static void print_event(struct iio_event_data *event) +{ + enum iio_chan_type type = IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event->id); + enum iio_modifier mod = IIO_EVENT_CODE_EXTRACT_MODIFIER(event->id); + enum iio_event_type ev_type = IIO_EVENT_CODE_EXTRACT_TYPE(event->id); + enum iio_event_direction dir = IIO_EVENT_CODE_EXTRACT_DIR(event->id); + int chan = IIO_EVENT_CODE_EXTRACT_CHAN(event->id); + int chan2 = IIO_EVENT_CODE_EXTRACT_CHAN2(event->id); + bool diff = IIO_EVENT_CODE_EXTRACT_DIFF(event->id); + + if (!event_is_known(event)) { + printf("Unknown event: time: %lld, id: %llx\n", + event->timestamp, event->id); + return; + } + + printf("Event: time: %lld, ", event->timestamp); + + if (mod != IIO_NO_MOD) { + printf("type: %s(%s), ", + iio_chan_type_name_spec[type], + iio_modifier_names[mod]); + } else { + printf("type: %s, ", + iio_chan_type_name_spec[type]); + } + + if (diff && chan >= 0 && chan2 >= 0) + printf("channel: %d-%d, ", chan, chan2); + else if (chan >= 0) + printf("channel: %d, ", chan); + + printf("evtype: %s", iio_ev_type_text[ev_type]); + + if (dir != IIO_EV_DIR_NONE) + printf(", direction: %s", iio_ev_dir_text[dir]); + printf("\n"); +} + +int main(int argc, char **argv) +{ + struct iio_event_data event; + const char *device_name; + char *chrdev_name; + int ret; + int dev_num; + int fd, event_fd; + + if (argc <= 1) { + printf("Usage: %s \n", argv[0]); + return -1; + } + + device_name = argv[1]; + + dev_num = find_type_by_name(device_name, "iio:device"); + if (dev_num >= 0) { + printf("Found IIO device with name %s with device number %d\n", + device_name, dev_num); + ret = asprintf(&chrdev_name, "/dev/iio:device%d", dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error_ret; + } + } else { + /* If we can't find a IIO device by name assume device_name is a + IIO chrdev */ + chrdev_name = strdup(device_name); + } + + fd = open(chrdev_name, 0); + if (fd == -1) { + fprintf(stdout, "Failed to open %s\n", chrdev_name); + ret = -errno; + goto error_free_chrdev_name; + } + + ret = ioctl(fd, IIO_GET_EVENT_FD_IOCTL, &event_fd); + + close(fd); + + if (ret == -1 || event_fd == -1) { + fprintf(stdout, "Failed to retrieve event fd\n"); + ret = -errno; + goto error_free_chrdev_name; + } + + while (true) { + ret = read(event_fd, &event, sizeof(event)); + if (ret == -1) { + if (errno == EAGAIN) { + printf("nothing available\n"); + continue; + } else { + perror("Failed to read event from device"); + ret = -errno; + break; + } + } + + print_event(&event); + } + + close(event_fd); +error_free_chrdev_name: + free(chrdev_name); +error_ret: + return ret; +} diff --git a/kernel/tools/iio/iio_utils.c b/kernel/tools/iio/iio_utils.c new file mode 100644 index 000000000..6f6452167 --- /dev/null +++ b/kernel/tools/iio/iio_utils.c @@ -0,0 +1,669 @@ +/* IIO - useful set of util functionality + * + * Copyright (c) 2008 Jonathan Cameron + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ +#ifndef _IIO_UTILS_H +#define _IIO_UTILS_H + +#include +#include +#include +#include +#include +#include +#include +#include "iio_utils.h" + +const char *iio_dir = "/sys/bus/iio/devices/"; + +static char * const iio_direction[] = { + "in", + "out", +}; + +/** + * iioutils_break_up_name() - extract generic name from full channel name + * @full_name: the full channel name + * @generic_name: the output generic channel name + **/ +int iioutils_break_up_name(const char *full_name, + char **generic_name) +{ + char *current; + char *w, *r; + char *working, *prefix = ""; + int i; + + for (i = 0; i < sizeof(iio_direction) / sizeof(iio_direction[0]); i++) + if (!strncmp(full_name, iio_direction[i], + strlen(iio_direction[i]))) { + prefix = iio_direction[i]; + break; + } + + current = strdup(full_name + strlen(prefix) + 1); + working = strtok(current, "_\0"); + + w = working; + r = working; + + while (*r != '\0') { + if (!isdigit(*r)) { + *w = *r; + w++; + } + r++; + } + *w = '\0'; + asprintf(generic_name, "%s_%s", prefix, working); + free(current); + + return 0; +} + +/** + * iioutils_get_type() - find and process _type attribute data + * @is_signed: output whether channel is signed + * @bytes: output how many bytes the channel storage occupies + * @mask: output a bit mask for the raw data + * @be: big endian + * @device_dir: the iio device directory + * @name: the channel name + * @generic_name: the channel type name + **/ +int iioutils_get_type(unsigned *is_signed, + unsigned *bytes, + unsigned *bits_used, + unsigned *shift, + uint64_t *mask, + unsigned *be, + const char *device_dir, + const char *name, + const char *generic_name) +{ + FILE *sysfsfp; + int ret; + DIR *dp; + char *scan_el_dir, *builtname, *builtname_generic, *filename = 0; + char signchar, endianchar; + unsigned padint; + const struct dirent *ent; + + ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir); + if (ret < 0) { + ret = -ENOMEM; + goto error_ret; + } + ret = asprintf(&builtname, FORMAT_TYPE_FILE, name); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_scan_el_dir; + } + ret = asprintf(&builtname_generic, FORMAT_TYPE_FILE, generic_name); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_builtname; + } + + dp = opendir(scan_el_dir); + if (dp == NULL) { + ret = -errno; + goto error_free_builtname_generic; + } + while (ent = readdir(dp), ent != NULL) + /* + * Do we allow devices to override a generic name with + * a specific one? + */ + if ((strcmp(builtname, ent->d_name) == 0) || + (strcmp(builtname_generic, ent->d_name) == 0)) { + ret = asprintf(&filename, + "%s/%s", scan_el_dir, ent->d_name); + if (ret < 0) { + ret = -ENOMEM; + goto error_closedir; + } + sysfsfp = fopen(filename, "r"); + if (sysfsfp == NULL) { + printf("failed to open %s\n", filename); + ret = -errno; + goto error_free_filename; + } + + ret = fscanf(sysfsfp, + "%ce:%c%u/%u>>%u", + &endianchar, + &signchar, + bits_used, + &padint, shift); + if (ret < 0) { + printf("failed to pass scan type description\n"); + ret = -errno; + goto error_close_sysfsfp; + } + *be = (endianchar == 'b'); + *bytes = padint / 8; + if (*bits_used == 64) + *mask = ~0; + else + *mask = (1 << *bits_used) - 1; + if (signchar == 's') + *is_signed = 1; + else + *is_signed = 0; + fclose(sysfsfp); + free(filename); + + filename = 0; + sysfsfp = 0; + } +error_close_sysfsfp: + if (sysfsfp) + fclose(sysfsfp); +error_free_filename: + if (filename) + free(filename); +error_closedir: + closedir(dp); +error_free_builtname_generic: + free(builtname_generic); +error_free_builtname: + free(builtname); +error_free_scan_el_dir: + free(scan_el_dir); +error_ret: + return ret; +} + +int iioutils_get_param_float(float *output, + const char *param_name, + const char *device_dir, + const char *name, + const char *generic_name) +{ + FILE *sysfsfp; + int ret; + DIR *dp; + char *builtname, *builtname_generic; + char *filename = NULL; + const struct dirent *ent; + + ret = asprintf(&builtname, "%s_%s", name, param_name); + if (ret < 0) { + ret = -ENOMEM; + goto error_ret; + } + ret = asprintf(&builtname_generic, + "%s_%s", generic_name, param_name); + if (ret < 0) { + ret = -ENOMEM; + goto error_free_builtname; + } + dp = opendir(device_dir); + if (dp == NULL) { + ret = -errno; + goto error_free_builtname_generic; + } + while (ent = readdir(dp), ent != NULL) + if ((strcmp(builtname, ent->d_name) == 0) || + (strcmp(builtname_generic, ent->d_name) == 0)) { + ret = asprintf(&filename, + "%s/%s", device_dir, ent->d_name); + if (ret < 0) { + ret = -ENOMEM; + goto error_closedir; + } + sysfsfp = fopen(filename, "r"); + if (!sysfsfp) { + ret = -errno; + goto error_free_filename; + } + fscanf(sysfsfp, "%f", output); + break; + } +error_free_filename: + if (filename) + free(filename); +error_closedir: + closedir(dp); +error_free_builtname_generic: + free(builtname_generic); +error_free_builtname: + free(builtname); +error_ret: + return ret; +} + +/** + * bsort_channel_array_by_index() - reorder so that the array is in index order + * + **/ + +void bsort_channel_array_by_index(struct iio_channel_info **ci_array, + int cnt) +{ + + struct iio_channel_info temp; + int x, y; + + for (x = 0; x < cnt; x++) + for (y = 0; y < (cnt - 1); y++) + if ((*ci_array)[y].index > (*ci_array)[y+1].index) { + temp = (*ci_array)[y + 1]; + (*ci_array)[y + 1] = (*ci_array)[y]; + (*ci_array)[y] = temp; + } +} + +/** + * build_channel_array() - function to figure out what channels are present + * @device_dir: the IIO device directory in sysfs + * @ + **/ +int build_channel_array(const char *device_dir, + struct iio_channel_info **ci_array, + int *counter) +{ + DIR *dp; + FILE *sysfsfp; + int count, i; + struct iio_channel_info *current; + int ret; + const struct dirent *ent; + char *scan_el_dir; + char *filename; + + *counter = 0; + ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir); + if (ret < 0) { + ret = -ENOMEM; + goto error_ret; + } + dp = opendir(scan_el_dir); + if (dp == NULL) { + ret = -errno; + goto error_free_name; + } + while (ent = readdir(dp), ent != NULL) + if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en"), + "_en") == 0) { + ret = asprintf(&filename, + "%s/%s", scan_el_dir, ent->d_name); + if (ret < 0) { + ret = -ENOMEM; + goto error_close_dir; + } + sysfsfp = fopen(filename, "r"); + if (sysfsfp == NULL) { + ret = -errno; + free(filename); + goto error_close_dir; + } + fscanf(sysfsfp, "%i", &ret); + if (ret == 1) + (*counter)++; + fclose(sysfsfp); + free(filename); + } + *ci_array = malloc(sizeof(**ci_array) * (*counter)); + if (*ci_array == NULL) { + ret = -ENOMEM; + goto error_close_dir; + } + seekdir(dp, 0); + count = 0; + while (ent = readdir(dp), ent != NULL) { + if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en"), + "_en") == 0) { + int current_enabled = 0; + + current = &(*ci_array)[count++]; + ret = asprintf(&filename, + "%s/%s", scan_el_dir, ent->d_name); + if (ret < 0) { + ret = -ENOMEM; + /* decrement count to avoid freeing name */ + count--; + goto error_cleanup_array; + } + sysfsfp = fopen(filename, "r"); + if (sysfsfp == NULL) { + free(filename); + ret = -errno; + goto error_cleanup_array; + } + fscanf(sysfsfp, "%i", ¤t_enabled); + fclose(sysfsfp); + + if (!current_enabled) { + free(filename); + count--; + continue; + } + + current->scale = 1.0; + current->offset = 0; + current->name = strndup(ent->d_name, + strlen(ent->d_name) - + strlen("_en")); + if (current->name == NULL) { + free(filename); + ret = -ENOMEM; + goto error_cleanup_array; + } + /* Get the generic and specific name elements */ + ret = iioutils_break_up_name(current->name, + ¤t->generic_name); + if (ret) { + free(filename); + goto error_cleanup_array; + } + ret = asprintf(&filename, + "%s/%s_index", + scan_el_dir, + current->name); + if (ret < 0) { + free(filename); + ret = -ENOMEM; + goto error_cleanup_array; + } + sysfsfp = fopen(filename, "r"); + fscanf(sysfsfp, "%u", ¤t->index); + fclose(sysfsfp); + free(filename); + /* Find the scale */ + ret = iioutils_get_param_float(¤t->scale, + "scale", + device_dir, + current->name, + current->generic_name); + if (ret < 0) + goto error_cleanup_array; + ret = iioutils_get_param_float(¤t->offset, + "offset", + device_dir, + current->name, + current->generic_name); + if (ret < 0) + goto error_cleanup_array; + ret = iioutils_get_type(¤t->is_signed, + ¤t->bytes, + ¤t->bits_used, + ¤t->shift, + ¤t->mask, + ¤t->be, + device_dir, + current->name, + current->generic_name); + } + } + + closedir(dp); + /* reorder so that the array is in index order */ + bsort_channel_array_by_index(ci_array, *counter); + + return 0; + +error_cleanup_array: + for (i = count - 1; i >= 0; i--) + free((*ci_array)[i].name); + free(*ci_array); +error_close_dir: + closedir(dp); +error_free_name: + free(scan_el_dir); +error_ret: + return ret; +} + +/** + * find_type_by_name() - function to match top level types by name + * @name: top level type instance name + * @type: the type of top level instance being sort + * + * Typical types this is used for are device and trigger. + **/ +int find_type_by_name(const char *name, const char *type) +{ + const struct dirent *ent; + int number, numstrlen; + + FILE *nameFile; + DIR *dp; + char thisname[IIO_MAX_NAME_LENGTH]; + char *filename; + + dp = opendir(iio_dir); + if (dp == NULL) { + printf("No industrialio devices available\n"); + return -ENODEV; + } + + while (ent = readdir(dp), ent != NULL) { + if (strcmp(ent->d_name, ".") != 0 && + strcmp(ent->d_name, "..") != 0 && + strlen(ent->d_name) > strlen(type) && + strncmp(ent->d_name, type, strlen(type)) == 0) { + numstrlen = sscanf(ent->d_name + strlen(type), + "%d", + &number); + /* verify the next character is not a colon */ + if (strncmp(ent->d_name + strlen(type) + numstrlen, + ":", + 1) != 0) { + filename = malloc(strlen(iio_dir) + + strlen(type) + + numstrlen + + 6); + if (filename == NULL) { + closedir(dp); + return -ENOMEM; + } + sprintf(filename, "%s%s%d/name", + iio_dir, + type, + number); + nameFile = fopen(filename, "r"); + if (!nameFile) { + free(filename); + continue; + } + free(filename); + fscanf(nameFile, "%s", thisname); + fclose(nameFile); + if (strcmp(name, thisname) == 0) { + closedir(dp); + return number; + } + } + } + } + closedir(dp); + return -ENODEV; +} + +int _write_sysfs_int(char *filename, char *basedir, int val, int verify) +{ + int ret = 0; + FILE *sysfsfp; + int test; + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + + if (temp == NULL) + return -ENOMEM; + sprintf(temp, "%s/%s", basedir, filename); + sysfsfp = fopen(temp, "w"); + if (sysfsfp == NULL) { + printf("failed to open %s\n", temp); + ret = -errno; + goto error_free; + } + fprintf(sysfsfp, "%d", val); + fclose(sysfsfp); + if (verify) { + sysfsfp = fopen(temp, "r"); + if (sysfsfp == NULL) { + printf("failed to open %s\n", temp); + ret = -errno; + goto error_free; + } + fscanf(sysfsfp, "%d", &test); + fclose(sysfsfp); + if (test != val) { + printf("Possible failure in int write %d to %s%s\n", + val, + basedir, + filename); + ret = -1; + } + } +error_free: + free(temp); + return ret; +} + +int write_sysfs_int(char *filename, char *basedir, int val) +{ + return _write_sysfs_int(filename, basedir, val, 0); +} + +int write_sysfs_int_and_verify(char *filename, char *basedir, int val) +{ + return _write_sysfs_int(filename, basedir, val, 1); +} + +int _write_sysfs_string(char *filename, char *basedir, char *val, int verify) +{ + int ret = 0; + FILE *sysfsfp; + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + + if (temp == NULL) { + printf("Memory allocation failed\n"); + return -ENOMEM; + } + sprintf(temp, "%s/%s", basedir, filename); + sysfsfp = fopen(temp, "w"); + if (sysfsfp == NULL) { + printf("Could not open %s\n", temp); + ret = -errno; + goto error_free; + } + fprintf(sysfsfp, "%s", val); + fclose(sysfsfp); + if (verify) { + sysfsfp = fopen(temp, "r"); + if (sysfsfp == NULL) { + printf("could not open file to verify\n"); + ret = -errno; + goto error_free; + } + fscanf(sysfsfp, "%s", temp); + fclose(sysfsfp); + if (strcmp(temp, val) != 0) { + printf("Possible failure in string write of %s " + "Should be %s " + "written to %s\%s\n", + temp, + val, + basedir, + filename); + ret = -1; + } + } +error_free: + free(temp); + + return ret; +} + +/** + * write_sysfs_string_and_verify() - string write, readback and verify + * @filename: name of file to write to + * @basedir: the sysfs directory in which the file is to be found + * @val: the string to write + **/ +int write_sysfs_string_and_verify(char *filename, char *basedir, char *val) +{ + return _write_sysfs_string(filename, basedir, val, 1); +} + +int write_sysfs_string(char *filename, char *basedir, char *val) +{ + return _write_sysfs_string(filename, basedir, val, 0); +} + +int read_sysfs_posint(char *filename, char *basedir) +{ + int ret; + FILE *sysfsfp; + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + + if (temp == NULL) { + printf("Memory allocation failed"); + return -ENOMEM; + } + sprintf(temp, "%s/%s", basedir, filename); + sysfsfp = fopen(temp, "r"); + if (sysfsfp == NULL) { + ret = -errno; + goto error_free; + } + fscanf(sysfsfp, "%d\n", &ret); + fclose(sysfsfp); +error_free: + free(temp); + return ret; +} + +int read_sysfs_float(char *filename, char *basedir, float *val) +{ + int ret = 0; + FILE *sysfsfp; + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + + if (temp == NULL) { + printf("Memory allocation failed"); + return -ENOMEM; + } + sprintf(temp, "%s/%s", basedir, filename); + sysfsfp = fopen(temp, "r"); + if (sysfsfp == NULL) { + ret = -errno; + goto error_free; + } + fscanf(sysfsfp, "%f\n", val); + fclose(sysfsfp); +error_free: + free(temp); + return ret; +} + +int read_sysfs_string(const char *filename, const char *basedir, char *str) +{ + int ret = 0; + FILE *sysfsfp; + char *temp = malloc(strlen(basedir) + strlen(filename) + 2); + + if (temp == NULL) { + printf("Memory allocation failed"); + return -ENOMEM; + } + sprintf(temp, "%s/%s", basedir, filename); + sysfsfp = fopen(temp, "r"); + if (sysfsfp == NULL) { + ret = -errno; + goto error_free; + } + fscanf(sysfsfp, "%s\n", str); + fclose(sysfsfp); +error_free: + free(temp); + return ret; +} + +#endif /* _IIO_UTILS_H */ diff --git a/kernel/tools/iio/iio_utils.h b/kernel/tools/iio/iio_utils.h new file mode 100644 index 000000000..1bc837b2d --- /dev/null +++ b/kernel/tools/iio/iio_utils.h @@ -0,0 +1,71 @@ +#ifndef _IIO_UTILS_H_ +#define _IIO_UTILS_H_ + +/* IIO - useful set of util functionality + * + * Copyright (c) 2008 Jonathan Cameron + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include + +/* Made up value to limit allocation sizes */ +#define IIO_MAX_NAME_LENGTH 30 + +#define FORMAT_SCAN_ELEMENTS_DIR "%s/scan_elements" +#define FORMAT_TYPE_FILE "%s_type" + +extern const char *iio_dir; + +/** + * struct iio_channel_info - information about a given channel + * @name: channel name + * @generic_name: general name for channel type + * @scale: scale factor to be applied for conversion to si units + * @offset: offset to be applied for conversion to si units + * @index: the channel index in the buffer output + * @bytes: number of bytes occupied in buffer output + * @mask: a bit mask for the raw output + * @is_signed: is the raw value stored signed + * @enabled: is this channel enabled + **/ +struct iio_channel_info { + char *name; + char *generic_name; + float scale; + float offset; + unsigned index; + unsigned bytes; + unsigned bits_used; + unsigned shift; + uint64_t mask; + unsigned be; + unsigned is_signed; + unsigned location; +}; + +int iioutils_break_up_name(const char *full_name, char **generic_name); +int iioutils_get_type(unsigned *is_signed, unsigned *bytes, + unsigned *bits_used, unsigned *shift, + uint64_t *mask, unsigned *be, + const char *device_dir, const char *name, + const char *generic_name); +int iioutils_get_param_float(float *output, const char *param_name, + const char *device_dir, const char *name, + const char *generic_name); +void bsort_channel_array_by_index(struct iio_channel_info **ci_array, int cnt); +int build_channel_array(const char *device_dir, + struct iio_channel_info **ci_array, int *counter); +int find_type_by_name(const char *name, const char *type); +int write_sysfs_int(char *filename, char *basedir, int val); +int write_sysfs_int_and_verify(char *filename, char *basedir, int val); +int write_sysfs_string_and_verify(char *filename, char *basedir, char *val); +int write_sysfs_string(char *filename, char *basedir, char *val); +int read_sysfs_posint(char *filename, char *basedir); +int read_sysfs_float(char *filename, char *basedir, float *val); +int read_sysfs_string(const char *filename, const char *basedir, char *str); + +#endif /* _IIO_UTILS_H_ */ diff --git a/kernel/tools/iio/lsiio.c b/kernel/tools/iio/lsiio.c new file mode 100644 index 000000000..c585440f8 --- /dev/null +++ b/kernel/tools/iio/lsiio.c @@ -0,0 +1,158 @@ +/* + * Industrial I/O utilities - lsiio.c + * + * Copyright (c) 2010 Manuel Stahl + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "iio_utils.h" + + +static enum verbosity { + VERBLEVEL_DEFAULT, /* 0 gives lspci behaviour */ + VERBLEVEL_SENSORS, /* 1 lists sensors */ +} verblevel = VERBLEVEL_DEFAULT; + +const char *type_device = "iio:device"; +const char *type_trigger = "trigger"; + + +static inline int check_prefix(const char *str, const char *prefix) +{ + return strlen(str) > strlen(prefix) && + strncmp(str, prefix, strlen(prefix)) == 0; +} + +static inline int check_postfix(const char *str, const char *postfix) +{ + return strlen(str) > strlen(postfix) && + strcmp(str + strlen(str) - strlen(postfix), postfix) == 0; +} + +static int dump_channels(const char *dev_dir_name) +{ + DIR *dp; + const struct dirent *ent; + + dp = opendir(dev_dir_name); + if (dp == NULL) + return -errno; + while (ent = readdir(dp), ent != NULL) + if (check_prefix(ent->d_name, "in_") && + check_postfix(ent->d_name, "_raw")) { + printf(" %-10s\n", ent->d_name); + } + + return 0; +} + +static int dump_one_device(const char *dev_dir_name) +{ + char name[IIO_MAX_NAME_LENGTH]; + int dev_idx; + int retval; + + retval = sscanf(dev_dir_name + strlen(iio_dir) + strlen(type_device), + "%i", &dev_idx); + if (retval != 1) + return -EINVAL; + read_sysfs_string("name", dev_dir_name, name); + printf("Device %03d: %s\n", dev_idx, name); + + if (verblevel >= VERBLEVEL_SENSORS) + return dump_channels(dev_dir_name); + return 0; +} + +static int dump_one_trigger(const char *dev_dir_name) +{ + char name[IIO_MAX_NAME_LENGTH]; + int dev_idx; + int retval; + + retval = sscanf(dev_dir_name + strlen(iio_dir) + strlen(type_trigger), + "%i", &dev_idx); + if (retval != 1) + return -EINVAL; + read_sysfs_string("name", dev_dir_name, name); + printf("Trigger %03d: %s\n", dev_idx, name); + return 0; +} + +static void dump_devices(void) +{ + const struct dirent *ent; + DIR *dp; + + dp = opendir(iio_dir); + if (dp == NULL) { + printf("No industrial I/O devices available\n"); + return; + } + + while (ent = readdir(dp), ent != NULL) { + if (check_prefix(ent->d_name, type_device)) { + char *dev_dir_name; + + asprintf(&dev_dir_name, "%s%s", iio_dir, ent->d_name); + dump_one_device(dev_dir_name); + free(dev_dir_name); + if (verblevel >= VERBLEVEL_SENSORS) + printf("\n"); + } + } + rewinddir(dp); + while (ent = readdir(dp), ent != NULL) { + if (check_prefix(ent->d_name, type_trigger)) { + char *dev_dir_name; + + asprintf(&dev_dir_name, "%s%s", iio_dir, ent->d_name); + dump_one_trigger(dev_dir_name); + free(dev_dir_name); + } + } + closedir(dp); +} + +int main(int argc, char **argv) +{ + int c, err = 0; + + while ((c = getopt(argc, argv, "d:D:v")) != EOF) { + switch (c) { + case 'v': + verblevel++; + break; + + case '?': + default: + err++; + break; + } + } + if (err || argc > optind) { + fprintf(stderr, "Usage: lsiio [options]...\n" + "List industrial I/O devices\n" + " -v, --verbose\n" + " Increase verbosity (may be given multiple times)\n" + ); + exit(1); + } + + dump_devices(); + + return 0; +} diff --git a/kernel/tools/include/asm-generic/bitops.h b/kernel/tools/include/asm-generic/bitops.h new file mode 100644 index 000000000..653d1bad7 --- /dev/null +++ b/kernel/tools/include/asm-generic/bitops.h @@ -0,0 +1,29 @@ +#ifndef __TOOLS_ASM_GENERIC_BITOPS_H +#define __TOOLS_ASM_GENERIC_BITOPS_H + +/* + * tools/ copied this from include/asm-generic/bitops.h, bit by bit as it needed + * some functions. + * + * For the benefit of those who are trying to port Linux to another + * architecture, here are some C-language equivalents. You should + * recode these in the native assembly language, if at all possible. + * + * C language equivalents written by Theodore Ts'o, 9/26/92 + */ + +#include +#include +#include +#include +#include + +#ifndef _TOOLS_LINUX_BITOPS_H_ +#error only can be included directly +#endif + +#include + +#include + +#endif /* __TOOLS_ASM_GENERIC_BITOPS_H */ diff --git a/kernel/tools/include/asm-generic/bitops/__ffs.h b/kernel/tools/include/asm-generic/bitops/__ffs.h new file mode 100644 index 000000000..c94175015 --- /dev/null +++ b/kernel/tools/include/asm-generic/bitops/__ffs.h @@ -0,0 +1,43 @@ +#ifndef _TOOLS_LINUX_ASM_GENERIC_BITOPS___FFS_H_ +#define _TOOLS_LINUX_ASM_GENERIC_BITOPS___FFS_H_ + +#include + +/** + * __ffs - find first bit in word. + * @word: The word to search + * + * Undefined if no bit exists, so code should check against 0 first. + */ +static __always_inline unsigned long __ffs(unsigned long word) +{ + int num = 0; + +#if __BITS_PER_LONG == 64 + if ((word & 0xffffffff) == 0) { + num += 32; + word >>= 32; + } +#endif + if ((word & 0xffff) == 0) { + num += 16; + word >>= 16; + } + if ((word & 0xff) == 0) { + num += 8; + word >>= 8; + } + if ((word & 0xf) == 0) { + num += 4; + word >>= 4; + } + if ((word & 0x3) == 0) { + num += 2; + word >>= 2; + } + if ((word & 0x1) == 0) + num += 1; + return num; +} + +#endif /* _TOOLS_LINUX_ASM_GENERIC_BITOPS___FFS_H_ */ diff --git a/kernel/tools/include/asm-generic/bitops/__fls.h b/kernel/tools/include/asm-generic/bitops/__fls.h new file mode 100644 index 000000000..2218b9add --- /dev/null +++ b/kernel/tools/include/asm-generic/bitops/__fls.h @@ -0,0 +1 @@ +#include <../../../../include/asm-generic/bitops/__fls.h> diff --git a/kernel/tools/include/asm-generic/bitops/arch_hweight.h b/kernel/tools/include/asm-generic/bitops/arch_hweight.h new file mode 100644 index 000000000..318bb2b20 --- /dev/null +++ b/kernel/tools/include/asm-generic/bitops/arch_hweight.h @@ -0,0 +1 @@ +#include "../../../../include/asm-generic/bitops/arch_hweight.h" diff --git a/kernel/tools/include/asm-generic/bitops/atomic.h b/kernel/tools/include/asm-generic/bitops/atomic.h new file mode 100644 index 000000000..4bccd7c3d --- /dev/null +++ b/kernel/tools/include/asm-generic/bitops/atomic.h @@ -0,0 +1,22 @@ +#ifndef _TOOLS_LINUX_ASM_GENERIC_BITOPS_ATOMIC_H_ +#define _TOOLS_LINUX_ASM_GENERIC_BITOPS_ATOMIC_H_ + +#include + +static inline void set_bit(int nr, unsigned long *addr) +{ + addr[nr / __BITS_PER_LONG] |= 1UL << (nr % __BITS_PER_LONG); +} + +static inline void clear_bit(int nr, unsigned long *addr) +{ + addr[nr / __BITS_PER_LONG] &= ~(1UL << (nr % __BITS_PER_LONG)); +} + +static __always_inline int test_bit(unsigned int nr, const unsigned long *addr) +{ + return ((1UL << (nr % __BITS_PER_LONG)) & + (((unsigned long *)addr)[nr / __BITS_PER_LONG])) != 0; +} + +#endif /* _TOOLS_LINUX_ASM_GENERIC_BITOPS_ATOMIC_H_ */ diff --git a/kernel/tools/include/asm-generic/bitops/const_hweight.h b/kernel/tools/include/asm-generic/bitops/const_hweight.h new file mode 100644 index 000000000..0afd644af --- /dev/null +++ b/kernel/tools/include/asm-generic/bitops/const_hweight.h @@ -0,0 +1 @@ +#include "../../../../include/asm-generic/bitops/const_hweight.h" diff --git a/kernel/tools/include/asm-generic/bitops/find.h b/kernel/tools/include/asm-generic/bitops/find.h new file mode 100644 index 000000000..31f51547f --- /dev/null +++ b/kernel/tools/include/asm-generic/bitops/find.h @@ -0,0 +1,33 @@ +#ifndef _TOOLS_LINUX_ASM_GENERIC_BITOPS_FIND_H_ +#define _TOOLS_LINUX_ASM_GENERIC_BITOPS_FIND_H_ + +#ifndef find_next_bit +/** + * find_next_bit - find the next set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The bitmap size in bits + * + * Returns the bit number for the next set bit + * If no bits are set, returns @size. + */ +extern unsigned long find_next_bit(const unsigned long *addr, unsigned long + size, unsigned long offset); +#endif + +#ifndef find_first_bit + +/** + * find_first_bit - find the first set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum number of bits to search + * + * Returns the bit number of the first set bit. + * If no bits are set, returns @size. + */ +extern unsigned long find_first_bit(const unsigned long *addr, + unsigned long size); + +#endif /* find_first_bit */ + +#endif /*_TOOLS_LINUX_ASM_GENERIC_BITOPS_FIND_H_ */ diff --git a/kernel/tools/include/asm-generic/bitops/fls.h b/kernel/tools/include/asm-generic/bitops/fls.h new file mode 100644 index 000000000..dbf711a28 --- /dev/null +++ b/kernel/tools/include/asm-generic/bitops/fls.h @@ -0,0 +1 @@ +#include <../../../../include/asm-generic/bitops/fls.h> diff --git a/kernel/tools/include/asm-generic/bitops/fls64.h b/kernel/tools/include/asm-generic/bitops/fls64.h new file mode 100644 index 000000000..980b1f63c --- /dev/null +++ b/kernel/tools/include/asm-generic/bitops/fls64.h @@ -0,0 +1 @@ +#include <../../../../include/asm-generic/bitops/fls64.h> diff --git a/kernel/tools/include/asm-generic/bitops/hweight.h b/kernel/tools/include/asm-generic/bitops/hweight.h new file mode 100644 index 000000000..290120c01 --- /dev/null +++ b/kernel/tools/include/asm-generic/bitops/hweight.h @@ -0,0 +1,7 @@ +#ifndef _TOOLS_LINUX_ASM_GENERIC_BITOPS_HWEIGHT_H_ +#define _TOOLS_LINUX_ASM_GENERIC_BITOPS_HWEIGHT_H_ + +#include +#include + +#endif /* _TOOLS_LINUX_ASM_GENERIC_BITOPS_HWEIGHT_H_ */ diff --git a/kernel/tools/include/asm/bug.h b/kernel/tools/include/asm/bug.h new file mode 100644 index 000000000..9e5f48469 --- /dev/null +++ b/kernel/tools/include/asm/bug.h @@ -0,0 +1,25 @@ +#ifndef _TOOLS_ASM_BUG_H +#define _TOOLS_ASM_BUG_H + +#include + +#define __WARN_printf(arg...) do { fprintf(stderr, arg); } while (0) + +#define WARN(condition, format...) ({ \ + int __ret_warn_on = !!(condition); \ + if (unlikely(__ret_warn_on)) \ + __WARN_printf(format); \ + unlikely(__ret_warn_on); \ +}) + +#define WARN_ONCE(condition, format...) ({ \ + static int __warned; \ + int __ret_warn_once = !!(condition); \ + \ + if (unlikely(__ret_warn_once)) \ + if (WARN(!__warned, format)) \ + __warned = 1; \ + unlikely(__ret_warn_once); \ +}) + +#endif /* _TOOLS_ASM_BUG_H */ diff --git a/kernel/tools/include/linux/bitops.h b/kernel/tools/include/linux/bitops.h new file mode 100644 index 000000000..5ad9ee1dd --- /dev/null +++ b/kernel/tools/include/linux/bitops.h @@ -0,0 +1,58 @@ +#ifndef _TOOLS_LINUX_BITOPS_H_ +#define _TOOLS_LINUX_BITOPS_H_ + +#include +#include +#include + +#ifndef __WORDSIZE +#define __WORDSIZE (__SIZEOF_LONG__ * 8) +#endif + +#define BITS_PER_LONG __WORDSIZE + +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) +#define BITS_PER_BYTE 8 +#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) +#define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) +#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) +#define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE) + +extern unsigned int __sw_hweight8(unsigned int w); +extern unsigned int __sw_hweight16(unsigned int w); +extern unsigned int __sw_hweight32(unsigned int w); +extern unsigned long __sw_hweight64(__u64 w); + +/* + * Include this here because some architectures need generic_ffs/fls in + * scope + * + * XXX: this needs to be asm/bitops.h, when we get to per arch optimizations + */ +#include + +#define for_each_set_bit(bit, addr, size) \ + for ((bit) = find_first_bit((addr), (size)); \ + (bit) < (size); \ + (bit) = find_next_bit((addr), (size), (bit) + 1)) + +/* same as for_each_set_bit() but use bit as value to start with */ +#define for_each_set_bit_from(bit, addr, size) \ + for ((bit) = find_next_bit((addr), (size), (bit)); \ + (bit) < (size); \ + (bit) = find_next_bit((addr), (size), (bit) + 1)) + +static inline unsigned long hweight_long(unsigned long w) +{ + return sizeof(w) == 4 ? hweight32(w) : hweight64(w); +} + +static inline unsigned fls_long(unsigned long l) +{ + if (sizeof(l) == 4) + return fls(l); + return fls64(l); +} + +#endif diff --git a/kernel/tools/include/linux/compiler.h b/kernel/tools/include/linux/compiler.h new file mode 100644 index 000000000..88461f09c --- /dev/null +++ b/kernel/tools/include/linux/compiler.h @@ -0,0 +1,40 @@ +#ifndef _TOOLS_LINUX_COMPILER_H_ +#define _TOOLS_LINUX_COMPILER_H_ + +#ifndef __always_inline +# define __always_inline inline __attribute__((always_inline)) +#endif + +#define __user + +#ifndef __attribute_const__ +# define __attribute_const__ +#endif + +#ifndef __maybe_unused +# define __maybe_unused __attribute__((unused)) +#endif + +#ifndef __packed +# define __packed __attribute__((__packed__)) +#endif + +#ifndef __force +# define __force +#endif + +#ifndef __weak +# define __weak __attribute__((weak)) +#endif + +#ifndef likely +# define likely(x) __builtin_expect(!!(x), 1) +#endif + +#ifndef unlikely +# define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) + +#endif /* _TOOLS_LINUX_COMPILER_H */ diff --git a/kernel/tools/include/linux/export.h b/kernel/tools/include/linux/export.h new file mode 100644 index 000000000..d07e586b9 --- /dev/null +++ b/kernel/tools/include/linux/export.h @@ -0,0 +1,10 @@ +#ifndef _TOOLS_LINUX_EXPORT_H_ +#define _TOOLS_LINUX_EXPORT_H_ + +#define EXPORT_SYMBOL(sym) +#define EXPORT_SYMBOL_GPL(sym) +#define EXPORT_SYMBOL_GPL_FUTURE(sym) +#define EXPORT_UNUSED_SYMBOL(sym) +#define EXPORT_UNUSED_SYMBOL_GPL(sym) + +#endif diff --git a/kernel/tools/include/linux/hash.h b/kernel/tools/include/linux/hash.h new file mode 100644 index 000000000..d026c6573 --- /dev/null +++ b/kernel/tools/include/linux/hash.h @@ -0,0 +1,5 @@ +#include "../../../include/linux/hash.h" + +#ifndef _TOOLS_LINUX_HASH_H +#define _TOOLS_LINUX_HASH_H +#endif diff --git a/kernel/tools/include/linux/log2.h b/kernel/tools/include/linux/log2.h new file mode 100644 index 000000000..41446668c --- /dev/null +++ b/kernel/tools/include/linux/log2.h @@ -0,0 +1,185 @@ +/* Integer base 2 logarithm calculation + * + * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _TOOLS_LINUX_LOG2_H +#define _TOOLS_LINUX_LOG2_H + +/* + * deal with unrepresentable constant logarithms + */ +extern __attribute__((const, noreturn)) +int ____ilog2_NaN(void); + +/* + * non-constant log of base 2 calculators + * - the arch may override these in asm/bitops.h if they can be implemented + * more efficiently than using fls() and fls64() + * - the arch is not required to handle n==0 if implementing the fallback + */ +static inline __attribute__((const)) +int __ilog2_u32(u32 n) +{ + return fls(n) - 1; +} + +static inline __attribute__((const)) +int __ilog2_u64(u64 n) +{ + return fls64(n) - 1; +} + +/* + * Determine whether some value is a power of two, where zero is + * *not* considered a power of two. + */ + +static inline __attribute__((const)) +bool is_power_of_2(unsigned long n) +{ + return (n != 0 && ((n & (n - 1)) == 0)); +} + +/* + * round up to nearest power of two + */ +static inline __attribute__((const)) +unsigned long __roundup_pow_of_two(unsigned long n) +{ + return 1UL << fls_long(n - 1); +} + +/* + * round down to nearest power of two + */ +static inline __attribute__((const)) +unsigned long __rounddown_pow_of_two(unsigned long n) +{ + return 1UL << (fls_long(n) - 1); +} + +/** + * ilog2 - log of base 2 of 32-bit or a 64-bit unsigned value + * @n - parameter + * + * constant-capable log of base 2 calculation + * - this can be used to initialise global variables from constant data, hence + * the massive ternary operator construction + * + * selects the appropriately-sized optimised version depending on sizeof(n) + */ +#define ilog2(n) \ +( \ + __builtin_constant_p(n) ? ( \ + (n) < 1 ? ____ilog2_NaN() : \ + (n) & (1ULL << 63) ? 63 : \ + (n) & (1ULL << 62) ? 62 : \ + (n) & (1ULL << 61) ? 61 : \ + (n) & (1ULL << 60) ? 60 : \ + (n) & (1ULL << 59) ? 59 : \ + (n) & (1ULL << 58) ? 58 : \ + (n) & (1ULL << 57) ? 57 : \ + (n) & (1ULL << 56) ? 56 : \ + (n) & (1ULL << 55) ? 55 : \ + (n) & (1ULL << 54) ? 54 : \ + (n) & (1ULL << 53) ? 53 : \ + (n) & (1ULL << 52) ? 52 : \ + (n) & (1ULL << 51) ? 51 : \ + (n) & (1ULL << 50) ? 50 : \ + (n) & (1ULL << 49) ? 49 : \ + (n) & (1ULL << 48) ? 48 : \ + (n) & (1ULL << 47) ? 47 : \ + (n) & (1ULL << 46) ? 46 : \ + (n) & (1ULL << 45) ? 45 : \ + (n) & (1ULL << 44) ? 44 : \ + (n) & (1ULL << 43) ? 43 : \ + (n) & (1ULL << 42) ? 42 : \ + (n) & (1ULL << 41) ? 41 : \ + (n) & (1ULL << 40) ? 40 : \ + (n) & (1ULL << 39) ? 39 : \ + (n) & (1ULL << 38) ? 38 : \ + (n) & (1ULL << 37) ? 37 : \ + (n) & (1ULL << 36) ? 36 : \ + (n) & (1ULL << 35) ? 35 : \ + (n) & (1ULL << 34) ? 34 : \ + (n) & (1ULL << 33) ? 33 : \ + (n) & (1ULL << 32) ? 32 : \ + (n) & (1ULL << 31) ? 31 : \ + (n) & (1ULL << 30) ? 30 : \ + (n) & (1ULL << 29) ? 29 : \ + (n) & (1ULL << 28) ? 28 : \ + (n) & (1ULL << 27) ? 27 : \ + (n) & (1ULL << 26) ? 26 : \ + (n) & (1ULL << 25) ? 25 : \ + (n) & (1ULL << 24) ? 24 : \ + (n) & (1ULL << 23) ? 23 : \ + (n) & (1ULL << 22) ? 22 : \ + (n) & (1ULL << 21) ? 21 : \ + (n) & (1ULL << 20) ? 20 : \ + (n) & (1ULL << 19) ? 19 : \ + (n) & (1ULL << 18) ? 18 : \ + (n) & (1ULL << 17) ? 17 : \ + (n) & (1ULL << 16) ? 16 : \ + (n) & (1ULL << 15) ? 15 : \ + (n) & (1ULL << 14) ? 14 : \ + (n) & (1ULL << 13) ? 13 : \ + (n) & (1ULL << 12) ? 12 : \ + (n) & (1ULL << 11) ? 11 : \ + (n) & (1ULL << 10) ? 10 : \ + (n) & (1ULL << 9) ? 9 : \ + (n) & (1ULL << 8) ? 8 : \ + (n) & (1ULL << 7) ? 7 : \ + (n) & (1ULL << 6) ? 6 : \ + (n) & (1ULL << 5) ? 5 : \ + (n) & (1ULL << 4) ? 4 : \ + (n) & (1ULL << 3) ? 3 : \ + (n) & (1ULL << 2) ? 2 : \ + (n) & (1ULL << 1) ? 1 : \ + (n) & (1ULL << 0) ? 0 : \ + ____ilog2_NaN() \ + ) : \ + (sizeof(n) <= 4) ? \ + __ilog2_u32(n) : \ + __ilog2_u64(n) \ + ) + +/** + * roundup_pow_of_two - round the given value up to nearest power of two + * @n - parameter + * + * round the given value up to the nearest power of two + * - the result is undefined when n == 0 + * - this can be used to initialise global variables from constant data + */ +#define roundup_pow_of_two(n) \ +( \ + __builtin_constant_p(n) ? ( \ + (n == 1) ? 1 : \ + (1UL << (ilog2((n) - 1) + 1)) \ + ) : \ + __roundup_pow_of_two(n) \ + ) + +/** + * rounddown_pow_of_two - round the given value down to nearest power of two + * @n - parameter + * + * round the given value down to the nearest power of two + * - the result is undefined when n == 0 + * - this can be used to initialise global variables from constant data + */ +#define rounddown_pow_of_two(n) \ +( \ + __builtin_constant_p(n) ? ( \ + (1UL << ilog2(n))) : \ + __rounddown_pow_of_two(n) \ + ) + +#endif /* _TOOLS_LINUX_LOG2_H */ diff --git a/kernel/tools/include/linux/types.h b/kernel/tools/include/linux/types.h new file mode 100644 index 000000000..b5cf25e05 --- /dev/null +++ b/kernel/tools/include/linux/types.h @@ -0,0 +1,75 @@ +#ifndef _TOOLS_LINUX_TYPES_H_ +#define _TOOLS_LINUX_TYPES_H_ + +#include +#include +#include + +#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */ +#include + +struct page; +struct kmem_cache; + +typedef enum { + GFP_KERNEL, + GFP_ATOMIC, + __GFP_HIGHMEM, + __GFP_HIGH +} gfp_t; + +/* + * We define u64 as uint64_t for every architecture + * so that we can print it with "%"PRIx64 without getting warnings. + * + * typedef __u64 u64; + * typedef __s64 s64; + */ +typedef uint64_t u64; +typedef int64_t s64; + +typedef __u32 u32; +typedef __s32 s32; + +typedef __u16 u16; +typedef __s16 s16; + +typedef __u8 u8; +typedef __s8 s8; + +#ifdef __CHECKER__ +#define __bitwise__ __attribute__((bitwise)) +#else +#define __bitwise__ +#endif +#ifdef __CHECK_ENDIAN__ +#define __bitwise __bitwise__ +#else +#define __bitwise +#endif + +#define __force +#define __user +#define __must_check +#define __cold + +typedef __u16 __bitwise __le16; +typedef __u16 __bitwise __be16; +typedef __u32 __bitwise __le32; +typedef __u32 __bitwise __be32; +typedef __u64 __bitwise __le64; +typedef __u64 __bitwise __be64; + +struct list_head { + struct list_head *next, *prev; +}; + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#endif /* _TOOLS_LINUX_TYPES_H_ */ diff --git a/kernel/tools/include/tools/be_byteshift.h b/kernel/tools/include/tools/be_byteshift.h new file mode 100644 index 000000000..84c17d836 --- /dev/null +++ b/kernel/tools/include/tools/be_byteshift.h @@ -0,0 +1,70 @@ +#ifndef _TOOLS_BE_BYTESHIFT_H +#define _TOOLS_BE_BYTESHIFT_H + +#include + +static inline uint16_t __get_unaligned_be16(const uint8_t *p) +{ + return p[0] << 8 | p[1]; +} + +static inline uint32_t __get_unaligned_be32(const uint8_t *p) +{ + return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +} + +static inline uint64_t __get_unaligned_be64(const uint8_t *p) +{ + return (uint64_t)__get_unaligned_be32(p) << 32 | + __get_unaligned_be32(p + 4); +} + +static inline void __put_unaligned_be16(uint16_t val, uint8_t *p) +{ + *p++ = val >> 8; + *p++ = val; +} + +static inline void __put_unaligned_be32(uint32_t val, uint8_t *p) +{ + __put_unaligned_be16(val >> 16, p); + __put_unaligned_be16(val, p + 2); +} + +static inline void __put_unaligned_be64(uint64_t val, uint8_t *p) +{ + __put_unaligned_be32(val >> 32, p); + __put_unaligned_be32(val, p + 4); +} + +static inline uint16_t get_unaligned_be16(const void *p) +{ + return __get_unaligned_be16((const uint8_t *)p); +} + +static inline uint32_t get_unaligned_be32(const void *p) +{ + return __get_unaligned_be32((const uint8_t *)p); +} + +static inline uint64_t get_unaligned_be64(const void *p) +{ + return __get_unaligned_be64((const uint8_t *)p); +} + +static inline void put_unaligned_be16(uint16_t val, void *p) +{ + __put_unaligned_be16(val, p); +} + +static inline void put_unaligned_be32(uint32_t val, void *p) +{ + __put_unaligned_be32(val, p); +} + +static inline void put_unaligned_be64(uint64_t val, void *p) +{ + __put_unaligned_be64(val, p); +} + +#endif /* _TOOLS_BE_BYTESHIFT_H */ diff --git a/kernel/tools/include/tools/endian.h b/kernel/tools/include/tools/endian.h new file mode 100644 index 000000000..800119400 --- /dev/null +++ b/kernel/tools/include/tools/endian.h @@ -0,0 +1,56 @@ +#ifndef _TOOLS_ENDIAN_H +#define _TOOLS_ENDIAN_H + +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +#ifndef htole16 +#define htole16(x) (x) +#endif +#ifndef htole32 +#define htole32(x) (x) +#endif +#ifndef htole64 +#define htole64(x) (x) +#endif + +#ifndef le16toh +#define le16toh(x) (x) +#endif + +#ifndef le32toh +#define le32toh(x) (x) +#endif + +#ifndef le64toh +#define le64toh(x) (x) +#endif + +#else /* __BYTE_ORDER */ + +#ifndef htole16 +#define htole16(x) __bswap_16(x) +#endif +#ifndef htole32 +#define htole32(x) __bswap_32(x) +#endif +#ifndef htole64 +#define htole64(x) __bswap_64(x) +#endif + +#ifndef le16toh +#define le16toh(x) __bswap_16(x) +#endif + +#ifndef le32toh +#define le32toh(x) __bswap_32(x) +#endif + +#ifndef le64toh +#define le64toh(x) __bswap_64(x) +#endif + +#endif + +#endif /* _TOOLS_ENDIAN_H */ diff --git a/kernel/tools/include/tools/le_byteshift.h b/kernel/tools/include/tools/le_byteshift.h new file mode 100644 index 000000000..8fe9f2488 --- /dev/null +++ b/kernel/tools/include/tools/le_byteshift.h @@ -0,0 +1,70 @@ +#ifndef _TOOLS_LE_BYTESHIFT_H +#define _TOOLS_LE_BYTESHIFT_H + +#include + +static inline uint16_t __get_unaligned_le16(const uint8_t *p) +{ + return p[0] | p[1] << 8; +} + +static inline uint32_t __get_unaligned_le32(const uint8_t *p) +{ + return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; +} + +static inline uint64_t __get_unaligned_le64(const uint8_t *p) +{ + return (uint64_t)__get_unaligned_le32(p + 4) << 32 | + __get_unaligned_le32(p); +} + +static inline void __put_unaligned_le16(uint16_t val, uint8_t *p) +{ + *p++ = val; + *p++ = val >> 8; +} + +static inline void __put_unaligned_le32(uint32_t val, uint8_t *p) +{ + __put_unaligned_le16(val >> 16, p + 2); + __put_unaligned_le16(val, p); +} + +static inline void __put_unaligned_le64(uint64_t val, uint8_t *p) +{ + __put_unaligned_le32(val >> 32, p + 4); + __put_unaligned_le32(val, p); +} + +static inline uint16_t get_unaligned_le16(const void *p) +{ + return __get_unaligned_le16((const uint8_t *)p); +} + +static inline uint32_t get_unaligned_le32(const void *p) +{ + return __get_unaligned_le32((const uint8_t *)p); +} + +static inline uint64_t get_unaligned_le64(const void *p) +{ + return __get_unaligned_le64((const uint8_t *)p); +} + +static inline void put_unaligned_le16(uint16_t val, void *p) +{ + __put_unaligned_le16(val, p); +} + +static inline void put_unaligned_le32(uint32_t val, void *p) +{ + __put_unaligned_le32(val, p); +} + +static inline void put_unaligned_le64(uint64_t val, void *p) +{ + __put_unaligned_le64(val, p); +} + +#endif /* _TOOLS_LE_BYTESHIFT_H */ diff --git a/kernel/tools/lguest/.gitignore b/kernel/tools/lguest/.gitignore new file mode 100644 index 000000000..115587fd5 --- /dev/null +++ b/kernel/tools/lguest/.gitignore @@ -0,0 +1 @@ +lguest diff --git a/kernel/tools/lguest/Makefile b/kernel/tools/lguest/Makefile new file mode 100644 index 000000000..a107b5e4d --- /dev/null +++ b/kernel/tools/lguest/Makefile @@ -0,0 +1,13 @@ +# This creates the demonstration utility "lguest" which runs a Linux guest. +CFLAGS:=-m32 -Wall -Wmissing-declarations -Wmissing-prototypes -O3 -U_FORTIFY_SOURCE -Iinclude + +all: lguest + +include/linux/virtio_types.h: ../../include/uapi/linux/virtio_types.h + mkdir -p include/linux 2>&1 || true + ln -sf ../../../../include/uapi/linux/virtio_types.h $@ + +lguest: include/linux/virtio_types.h + +clean: + rm -f lguest diff --git a/kernel/tools/lguest/extract b/kernel/tools/lguest/extract new file mode 100644 index 000000000..7730bb6e4 --- /dev/null +++ b/kernel/tools/lguest/extract @@ -0,0 +1,58 @@ +#! /bin/sh + +set -e + +PREFIX=$1 +shift + +trap 'rm -r $TMPDIR' 0 +TMPDIR=`mktemp -d` + +exec 3>/dev/null +for f; do + while IFS=" +" read -r LINE; do + case "$LINE" in + *$PREFIX:[0-9]*:\**) + NUM=`echo "$LINE" | sed "s/.*$PREFIX:\([0-9]*\).*/\1/"` + if [ -f $TMPDIR/$NUM ]; then + echo "$TMPDIR/$NUM already exits prior to $f" + exit 1 + fi + exec 3>>$TMPDIR/$NUM + echo $f | sed 's,\.\./,,g' > $TMPDIR/.$NUM + /bin/echo "$LINE" | sed -e "s/$PREFIX:[0-9]*//" -e "s/:\*/*/" >&3 + ;; + *$PREFIX:[0-9]*) + NUM=`echo "$LINE" | sed "s/.*$PREFIX:\([0-9]*\).*/\1/"` + if [ -f $TMPDIR/$NUM ]; then + echo "$TMPDIR/$NUM already exits prior to $f" + exit 1 + fi + exec 3>>$TMPDIR/$NUM + echo $f | sed 's,\.\./,,g' > $TMPDIR/.$NUM + /bin/echo "$LINE" | sed "s/$PREFIX:[0-9]*//" >&3 + ;; + *:\**) + /bin/echo "$LINE" | sed -e "s/:\*/*/" -e "s,/\*\*/,," >&3 + echo >&3 + exec 3>/dev/null + ;; + *) + /bin/echo "$LINE" >&3 + ;; + esac + done < $f + echo >&3 + exec 3>/dev/null +done + +LASTFILE="" +for f in $TMPDIR/*; do + if [ "$LASTFILE" != $(cat $TMPDIR/.$(basename $f) ) ]; then + LASTFILE=$(cat $TMPDIR/.$(basename $f) ) + echo "[ $LASTFILE ]" + fi + cat $f +done + diff --git a/kernel/tools/lguest/lguest.c b/kernel/tools/lguest/lguest.c new file mode 100644 index 000000000..e44052483 --- /dev/null +++ b/kernel/tools/lguest/lguest.c @@ -0,0 +1,3410 @@ +/*P:100 + * This is the Launcher code, a simple program which lays out the "physical" + * memory for the new Guest by mapping the kernel image and the virtual + * devices, then opens /dev/lguest to tell the kernel about the Guest and + * control it. +:*/ +#define _LARGEFILE64_SOURCE +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef VIRTIO_F_ANY_LAYOUT +#define VIRTIO_F_ANY_LAYOUT 27 +#endif + +/*L:110 + * We can ignore the 43 include files we need for this program, but I do want + * to draw attention to the use of kernel-style types. + * + * As Linus said, "C is a Spartan language, and so should your naming be." I + * like these abbreviations, so we define them here. Note that u64 is always + * unsigned long long, which works on all Linux systems: this means that we can + * use %llu in printf for any u64. + */ +typedef unsigned long long u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +/*:*/ + +#define VIRTIO_CONFIG_NO_LEGACY +#define VIRTIO_PCI_NO_LEGACY +#define VIRTIO_BLK_NO_LEGACY +#define VIRTIO_NET_NO_LEGACY + +/* Use in-kernel ones, which defines VIRTIO_F_VERSION_1 */ +#include "../../include/uapi/linux/virtio_config.h" +#include "../../include/uapi/linux/virtio_net.h" +#include "../../include/uapi/linux/virtio_blk.h" +#include "../../include/uapi/linux/virtio_console.h" +#include "../../include/uapi/linux/virtio_rng.h" +#include +#include "../../include/uapi/linux/virtio_pci.h" +#include +#include "../../include/linux/lguest_launcher.h" + +#define BRIDGE_PFX "bridge:" +#ifndef SIOCBRADDIF +#define SIOCBRADDIF 0x89a2 /* add interface to bridge */ +#endif +/* We can have up to 256 pages for devices. */ +#define DEVICE_PAGES 256 +/* This will occupy 3 pages: it must be a power of 2. */ +#define VIRTQUEUE_NUM 256 + +/*L:120 + * verbose is both a global flag and a macro. The C preprocessor allows + * this, and although I wouldn't recommend it, it works quite nicely here. + */ +static bool verbose; +#define verbose(args...) \ + do { if (verbose) printf(args); } while(0) +/*:*/ + +/* The pointer to the start of guest memory. */ +static void *guest_base; +/* The maximum guest physical address allowed, and maximum possible. */ +static unsigned long guest_limit, guest_max, guest_mmio; +/* The /dev/lguest file descriptor. */ +static int lguest_fd; + +/* a per-cpu variable indicating whose vcpu is currently running */ +static unsigned int __thread cpu_id; + +/* 5 bit device number in the PCI_CONFIG_ADDR => 32 only */ +#define MAX_PCI_DEVICES 32 + +/* This is our list of devices. */ +struct device_list { + /* Counter to assign interrupt numbers. */ + unsigned int next_irq; + + /* Counter to print out convenient device numbers. */ + unsigned int device_num; + + /* PCI devices. */ + struct device *pci[MAX_PCI_DEVICES]; +}; + +/* The list of Guest devices, based on command line arguments. */ +static struct device_list devices; + +struct virtio_pci_cfg_cap { + struct virtio_pci_cap cap; + u32 pci_cfg_data; /* Data for BAR access. */ +}; + +struct virtio_pci_mmio { + struct virtio_pci_common_cfg cfg; + u16 notify; + u8 isr; + u8 padding; + /* Device-specific configuration follows this. */ +}; + +/* This is the layout (little-endian) of the PCI config space. */ +struct pci_config { + u16 vendor_id, device_id; + u16 command, status; + u8 revid, prog_if, subclass, class; + u8 cacheline_size, lat_timer, header_type, bist; + u32 bar[6]; + u32 cardbus_cis_ptr; + u16 subsystem_vendor_id, subsystem_device_id; + u32 expansion_rom_addr; + u8 capabilities, reserved1[3]; + u32 reserved2; + u8 irq_line, irq_pin, min_grant, max_latency; + + /* Now, this is the linked capability list. */ + struct virtio_pci_cap common; + struct virtio_pci_notify_cap notify; + struct virtio_pci_cap isr; + struct virtio_pci_cap device; + struct virtio_pci_cfg_cap cfg_access; +}; + +/* The device structure describes a single device. */ +struct device { + /* The name of this device, for --verbose. */ + const char *name; + + /* Any queues attached to this device */ + struct virtqueue *vq; + + /* Is it operational */ + bool running; + + /* Has it written FEATURES_OK but not re-checked it? */ + bool wrote_features_ok; + + /* PCI configuration */ + union { + struct pci_config config; + u32 config_words[sizeof(struct pci_config) / sizeof(u32)]; + }; + + /* Features we offer, and those accepted. */ + u64 features, features_accepted; + + /* Device-specific config hangs off the end of this. */ + struct virtio_pci_mmio *mmio; + + /* PCI MMIO resources (all in BAR0) */ + size_t mmio_size; + u32 mmio_addr; + + /* Device-specific data. */ + void *priv; +}; + +/* The virtqueue structure describes a queue attached to a device. */ +struct virtqueue { + struct virtqueue *next; + + /* Which device owns me. */ + struct device *dev; + + /* Name for printing errors. */ + const char *name; + + /* The actual ring of buffers. */ + struct vring vring; + + /* The information about this virtqueue (we only use queue_size on) */ + struct virtio_pci_common_cfg pci_config; + + /* Last available index we saw. */ + u16 last_avail_idx; + + /* How many are used since we sent last irq? */ + unsigned int pending_used; + + /* Eventfd where Guest notifications arrive. */ + int eventfd; + + /* Function for the thread which is servicing this virtqueue. */ + void (*service)(struct virtqueue *vq); + pid_t thread; +}; + +/* Remember the arguments to the program so we can "reboot" */ +static char **main_args; + +/* The original tty settings to restore on exit. */ +static struct termios orig_term; + +/* + * We have to be careful with barriers: our devices are all run in separate + * threads and so we need to make sure that changes visible to the Guest happen + * in precise order. + */ +#define wmb() __asm__ __volatile__("" : : : "memory") +#define rmb() __asm__ __volatile__("lock; addl $0,0(%%esp)" : : : "memory") +#define mb() __asm__ __volatile__("lock; addl $0,0(%%esp)" : : : "memory") + +/* Wrapper for the last available index. Makes it easier to change. */ +#define lg_last_avail(vq) ((vq)->last_avail_idx) + +/* + * The virtio configuration space is defined to be little-endian. x86 is + * little-endian too, but it's nice to be explicit so we have these helpers. + */ +#define cpu_to_le16(v16) (v16) +#define cpu_to_le32(v32) (v32) +#define cpu_to_le64(v64) (v64) +#define le16_to_cpu(v16) (v16) +#define le32_to_cpu(v32) (v32) +#define le64_to_cpu(v64) (v64) + +/* + * A real device would ignore weird/non-compliant driver behaviour. We + * stop and flag it, to help debugging Linux problems. + */ +#define bad_driver(d, fmt, ...) \ + errx(1, "%s: bad driver: " fmt, (d)->name, ## __VA_ARGS__) +#define bad_driver_vq(vq, fmt, ...) \ + errx(1, "%s vq %s: bad driver: " fmt, (vq)->dev->name, \ + vq->name, ## __VA_ARGS__) + +/* Is this iovec empty? */ +static bool iov_empty(const struct iovec iov[], unsigned int num_iov) +{ + unsigned int i; + + for (i = 0; i < num_iov; i++) + if (iov[i].iov_len) + return false; + return true; +} + +/* Take len bytes from the front of this iovec. */ +static void iov_consume(struct device *d, + struct iovec iov[], unsigned num_iov, + void *dest, unsigned len) +{ + unsigned int i; + + for (i = 0; i < num_iov; i++) { + unsigned int used; + + used = iov[i].iov_len < len ? iov[i].iov_len : len; + if (dest) { + memcpy(dest, iov[i].iov_base, used); + dest += used; + } + iov[i].iov_base += used; + iov[i].iov_len -= used; + len -= used; + } + if (len != 0) + bad_driver(d, "iovec too short!"); +} + +/*L:100 + * The Launcher code itself takes us out into userspace, that scary place where + * pointers run wild and free! Unfortunately, like most userspace programs, + * it's quite boring (which is why everyone likes to hack on the kernel!). + * Perhaps if you make up an Lguest Drinking Game at this point, it will get + * you through this section. Or, maybe not. + * + * The Launcher sets up a big chunk of memory to be the Guest's "physical" + * memory and stores it in "guest_base". In other words, Guest physical == + * Launcher virtual with an offset. + * + * This can be tough to get your head around, but usually it just means that we + * use these trivial conversion functions when the Guest gives us its + * "physical" addresses: + */ +static void *from_guest_phys(unsigned long addr) +{ + return guest_base + addr; +} + +static unsigned long to_guest_phys(const void *addr) +{ + return (addr - guest_base); +} + +/*L:130 + * Loading the Kernel. + * + * We start with couple of simple helper routines. open_or_die() avoids + * error-checking code cluttering the callers: + */ +static int open_or_die(const char *name, int flags) +{ + int fd = open(name, flags); + if (fd < 0) + err(1, "Failed to open %s", name); + return fd; +} + +/* map_zeroed_pages() takes a number of pages. */ +static void *map_zeroed_pages(unsigned int num) +{ + int fd = open_or_die("/dev/zero", O_RDONLY); + void *addr; + + /* + * We use a private mapping (ie. if we write to the page, it will be + * copied). We allocate an extra two pages PROT_NONE to act as guard + * pages against read/write attempts that exceed allocated space. + */ + addr = mmap(NULL, getpagesize() * (num+2), + PROT_NONE, MAP_PRIVATE, fd, 0); + + if (addr == MAP_FAILED) + err(1, "Mmapping %u pages of /dev/zero", num); + + if (mprotect(addr + getpagesize(), getpagesize() * num, + PROT_READ|PROT_WRITE) == -1) + err(1, "mprotect rw %u pages failed", num); + + /* + * One neat mmap feature is that you can close the fd, and it + * stays mapped. + */ + close(fd); + + /* Return address after PROT_NONE page */ + return addr + getpagesize(); +} + +/* Get some bytes which won't be mapped into the guest. */ +static unsigned long get_mmio_region(size_t size) +{ + unsigned long addr = guest_mmio; + size_t i; + + if (!size) + return addr; + + /* Size has to be a power of 2 (and multiple of 16) */ + for (i = 1; i < size; i <<= 1); + + guest_mmio += i; + + return addr; +} + +/* + * This routine is used to load the kernel or initrd. It tries mmap, but if + * that fails (Plan 9's kernel file isn't nicely aligned on page boundaries), + * it falls back to reading the memory in. + */ +static void map_at(int fd, void *addr, unsigned long offset, unsigned long len) +{ + ssize_t r; + + /* + * We map writable even though for some segments are marked read-only. + * The kernel really wants to be writable: it patches its own + * instructions. + * + * MAP_PRIVATE means that the page won't be copied until a write is + * done to it. This allows us to share untouched memory between + * Guests. + */ + if (mmap(addr, len, PROT_READ|PROT_WRITE, + MAP_FIXED|MAP_PRIVATE, fd, offset) != MAP_FAILED) + return; + + /* pread does a seek and a read in one shot: saves a few lines. */ + r = pread(fd, addr, len, offset); + if (r != len) + err(1, "Reading offset %lu len %lu gave %zi", offset, len, r); +} + +/* + * This routine takes an open vmlinux image, which is in ELF, and maps it into + * the Guest memory. ELF = Embedded Linking Format, which is the format used + * by all modern binaries on Linux including the kernel. + * + * The ELF headers give *two* addresses: a physical address, and a virtual + * address. We use the physical address; the Guest will map itself to the + * virtual address. + * + * We return the starting address. + */ +static unsigned long map_elf(int elf_fd, const Elf32_Ehdr *ehdr) +{ + Elf32_Phdr phdr[ehdr->e_phnum]; + unsigned int i; + + /* + * Sanity checks on the main ELF header: an x86 executable with a + * reasonable number of correctly-sized program headers. + */ + if (ehdr->e_type != ET_EXEC + || ehdr->e_machine != EM_386 + || ehdr->e_phentsize != sizeof(Elf32_Phdr) + || ehdr->e_phnum < 1 || ehdr->e_phnum > 65536U/sizeof(Elf32_Phdr)) + errx(1, "Malformed elf header"); + + /* + * An ELF executable contains an ELF header and a number of "program" + * headers which indicate which parts ("segments") of the program to + * load where. + */ + + /* We read in all the program headers at once: */ + if (lseek(elf_fd, ehdr->e_phoff, SEEK_SET) < 0) + err(1, "Seeking to program headers"); + if (read(elf_fd, phdr, sizeof(phdr)) != sizeof(phdr)) + err(1, "Reading program headers"); + + /* + * Try all the headers: there are usually only three. A read-only one, + * a read-write one, and a "note" section which we don't load. + */ + for (i = 0; i < ehdr->e_phnum; i++) { + /* If this isn't a loadable segment, we ignore it */ + if (phdr[i].p_type != PT_LOAD) + continue; + + verbose("Section %i: size %i addr %p\n", + i, phdr[i].p_memsz, (void *)phdr[i].p_paddr); + + /* We map this section of the file at its physical address. */ + map_at(elf_fd, from_guest_phys(phdr[i].p_paddr), + phdr[i].p_offset, phdr[i].p_filesz); + } + + /* The entry point is given in the ELF header. */ + return ehdr->e_entry; +} + +/*L:150 + * A bzImage, unlike an ELF file, is not meant to be loaded. You're supposed + * to jump into it and it will unpack itself. We used to have to perform some + * hairy magic because the unpacking code scared me. + * + * Fortunately, Jeremy Fitzhardinge convinced me it wasn't that hard and wrote + * a small patch to jump over the tricky bits in the Guest, so now we just read + * the funky header so we know where in the file to load, and away we go! + */ +static unsigned long load_bzimage(int fd) +{ + struct boot_params boot; + int r; + /* Modern bzImages get loaded at 1M. */ + void *p = from_guest_phys(0x100000); + + /* + * Go back to the start of the file and read the header. It should be + * a Linux boot header (see Documentation/x86/boot.txt) + */ + lseek(fd, 0, SEEK_SET); + read(fd, &boot, sizeof(boot)); + + /* Inside the setup_hdr, we expect the magic "HdrS" */ + if (memcmp(&boot.hdr.header, "HdrS", 4) != 0) + errx(1, "This doesn't look like a bzImage to me"); + + /* Skip over the extra sectors of the header. */ + lseek(fd, (boot.hdr.setup_sects+1) * 512, SEEK_SET); + + /* Now read everything into memory. in nice big chunks. */ + while ((r = read(fd, p, 65536)) > 0) + p += r; + + /* Finally, code32_start tells us where to enter the kernel. */ + return boot.hdr.code32_start; +} + +/*L:140 + * Loading the kernel is easy when it's a "vmlinux", but most kernels + * come wrapped up in the self-decompressing "bzImage" format. With a little + * work, we can load those, too. + */ +static unsigned long load_kernel(int fd) +{ + Elf32_Ehdr hdr; + + /* Read in the first few bytes. */ + if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) + err(1, "Reading kernel"); + + /* If it's an ELF file, it starts with "\177ELF" */ + if (memcmp(hdr.e_ident, ELFMAG, SELFMAG) == 0) + return map_elf(fd, &hdr); + + /* Otherwise we assume it's a bzImage, and try to load it. */ + return load_bzimage(fd); +} + +/* + * This is a trivial little helper to align pages. Andi Kleen hated it because + * it calls getpagesize() twice: "it's dumb code." + * + * Kernel guys get really het up about optimization, even when it's not + * necessary. I leave this code as a reaction against that. + */ +static inline unsigned long page_align(unsigned long addr) +{ + /* Add upwards and truncate downwards. */ + return ((addr + getpagesize()-1) & ~(getpagesize()-1)); +} + +/*L:180 + * An "initial ram disk" is a disk image loaded into memory along with the + * kernel which the kernel can use to boot from without needing any drivers. + * Most distributions now use this as standard: the initrd contains the code to + * load the appropriate driver modules for the current machine. + * + * Importantly, James Morris works for RedHat, and Fedora uses initrds for its + * kernels. He sent me this (and tells me when I break it). + */ +static unsigned long load_initrd(const char *name, unsigned long mem) +{ + int ifd; + struct stat st; + unsigned long len; + + ifd = open_or_die(name, O_RDONLY); + /* fstat() is needed to get the file size. */ + if (fstat(ifd, &st) < 0) + err(1, "fstat() on initrd '%s'", name); + + /* + * We map the initrd at the top of memory, but mmap wants it to be + * page-aligned, so we round the size up for that. + */ + len = page_align(st.st_size); + map_at(ifd, from_guest_phys(mem - len), 0, st.st_size); + /* + * Once a file is mapped, you can close the file descriptor. It's a + * little odd, but quite useful. + */ + close(ifd); + verbose("mapped initrd %s size=%lu @ %p\n", name, len, (void*)mem-len); + + /* We return the initrd size. */ + return len; +} +/*:*/ + +/* + * Simple routine to roll all the commandline arguments together with spaces + * between them. + */ +static void concat(char *dst, char *args[]) +{ + unsigned int i, len = 0; + + for (i = 0; args[i]; i++) { + if (i) { + strcat(dst+len, " "); + len++; + } + strcpy(dst+len, args[i]); + len += strlen(args[i]); + } + /* In case it's empty. */ + dst[len] = '\0'; +} + +/*L:185 + * This is where we actually tell the kernel to initialize the Guest. We + * saw the arguments it expects when we looked at initialize() in lguest_user.c: + * the base of Guest "physical" memory, the top physical page to allow and the + * entry point for the Guest. + */ +static void tell_kernel(unsigned long start) +{ + unsigned long args[] = { LHREQ_INITIALIZE, + (unsigned long)guest_base, + guest_limit / getpagesize(), start, + (guest_mmio+getpagesize()-1) / getpagesize() }; + verbose("Guest: %p - %p (%#lx, MMIO %#lx)\n", + guest_base, guest_base + guest_limit, + guest_limit, guest_mmio); + lguest_fd = open_or_die("/dev/lguest", O_RDWR); + if (write(lguest_fd, args, sizeof(args)) < 0) + err(1, "Writing to /dev/lguest"); +} +/*:*/ + +/*L:200 + * Device Handling. + * + * When the Guest gives us a buffer, it sends an array of addresses and sizes. + * We need to make sure it's not trying to reach into the Launcher itself, so + * we have a convenient routine which checks it and exits with an error message + * if something funny is going on: + */ +static void *_check_pointer(struct device *d, + unsigned long addr, unsigned int size, + unsigned int line) +{ + /* + * Check if the requested address and size exceeds the allocated memory, + * or addr + size wraps around. + */ + if ((addr + size) > guest_limit || (addr + size) < addr) + bad_driver(d, "%s:%i: Invalid address %#lx", + __FILE__, line, addr); + /* + * We return a pointer for the caller's convenience, now we know it's + * safe to use. + */ + return from_guest_phys(addr); +} +/* A macro which transparently hands the line number to the real function. */ +#define check_pointer(d,addr,size) _check_pointer(d, addr, size, __LINE__) + +/* + * Each buffer in the virtqueues is actually a chain of descriptors. This + * function returns the next descriptor in the chain, or vq->vring.num if we're + * at the end. + */ +static unsigned next_desc(struct device *d, struct vring_desc *desc, + unsigned int i, unsigned int max) +{ + unsigned int next; + + /* If this descriptor says it doesn't chain, we're done. */ + if (!(desc[i].flags & VRING_DESC_F_NEXT)) + return max; + + /* Check they're not leading us off end of descriptors. */ + next = desc[i].next; + /* Make sure compiler knows to grab that: we don't want it changing! */ + wmb(); + + if (next >= max) + bad_driver(d, "Desc next is %u", next); + + return next; +} + +/* + * This actually sends the interrupt for this virtqueue, if we've used a + * buffer. + */ +static void trigger_irq(struct virtqueue *vq) +{ + unsigned long buf[] = { LHREQ_IRQ, vq->dev->config.irq_line }; + + /* Don't inform them if nothing used. */ + if (!vq->pending_used) + return; + vq->pending_used = 0; + + /* + * 2.4.7.1: + * + * If the VIRTIO_F_EVENT_IDX feature bit is not negotiated: + * The driver MUST set flags to 0 or 1. + */ + if (vq->vring.avail->flags > 1) + bad_driver_vq(vq, "avail->flags = %u\n", vq->vring.avail->flags); + + /* + * 2.4.7.2: + * + * If the VIRTIO_F_EVENT_IDX feature bit is not negotiated: + * + * - The device MUST ignore the used_event value. + * - After the device writes a descriptor index into the used ring: + * - If flags is 1, the device SHOULD NOT send an interrupt. + * - If flags is 0, the device MUST send an interrupt. + */ + if (vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) { + return; + } + + /* + * 4.1.4.5.1: + * + * If MSI-X capability is disabled, the device MUST set the Queue + * Interrupt bit in ISR status before sending a virtqueue notification + * to the driver. + */ + vq->dev->mmio->isr = 0x1; + + /* Send the Guest an interrupt tell them we used something up. */ + if (write(lguest_fd, buf, sizeof(buf)) != 0) + err(1, "Triggering irq %i", vq->dev->config.irq_line); +} + +/* + * This looks in the virtqueue for the first available buffer, and converts + * it to an iovec for convenient access. Since descriptors consist of some + * number of output then some number of input descriptors, it's actually two + * iovecs, but we pack them into one and note how many of each there were. + * + * This function waits if necessary, and returns the descriptor number found. + */ +static unsigned wait_for_vq_desc(struct virtqueue *vq, + struct iovec iov[], + unsigned int *out_num, unsigned int *in_num) +{ + unsigned int i, head, max; + struct vring_desc *desc; + u16 last_avail = lg_last_avail(vq); + + /* + * 2.4.7.1: + * + * The driver MUST handle spurious interrupts from the device. + * + * That's why this is a while loop. + */ + + /* There's nothing available? */ + while (last_avail == vq->vring.avail->idx) { + u64 event; + + /* + * Since we're about to sleep, now is a good time to tell the + * Guest about what we've used up to now. + */ + trigger_irq(vq); + + /* OK, now we need to know about added descriptors. */ + vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY; + + /* + * They could have slipped one in as we were doing that: make + * sure it's written, then check again. + */ + mb(); + if (last_avail != vq->vring.avail->idx) { + vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY; + break; + } + + /* Nothing new? Wait for eventfd to tell us they refilled. */ + if (read(vq->eventfd, &event, sizeof(event)) != sizeof(event)) + errx(1, "Event read failed?"); + + /* We don't need to be notified again. */ + vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY; + } + + /* Check it isn't doing very strange things with descriptor numbers. */ + if ((u16)(vq->vring.avail->idx - last_avail) > vq->vring.num) + bad_driver_vq(vq, "Guest moved used index from %u to %u", + last_avail, vq->vring.avail->idx); + + /* + * Make sure we read the descriptor number *after* we read the ring + * update; don't let the cpu or compiler change the order. + */ + rmb(); + + /* + * Grab the next descriptor number they're advertising, and increment + * the index we've seen. + */ + head = vq->vring.avail->ring[last_avail % vq->vring.num]; + lg_last_avail(vq)++; + + /* If their number is silly, that's a fatal mistake. */ + if (head >= vq->vring.num) + bad_driver_vq(vq, "Guest says index %u is available", head); + + /* When we start there are none of either input nor output. */ + *out_num = *in_num = 0; + + max = vq->vring.num; + desc = vq->vring.desc; + i = head; + + /* + * We have to read the descriptor after we read the descriptor number, + * but there's a data dependency there so the CPU shouldn't reorder + * that: no rmb() required. + */ + + do { + /* + * If this is an indirect entry, then this buffer contains a + * descriptor table which we handle as if it's any normal + * descriptor chain. + */ + if (desc[i].flags & VRING_DESC_F_INDIRECT) { + /* 2.4.5.3.1: + * + * The driver MUST NOT set the VIRTQ_DESC_F_INDIRECT + * flag unless the VIRTIO_F_INDIRECT_DESC feature was + * negotiated. + */ + if (!(vq->dev->features_accepted & + (1<vring.desc) + bad_driver_vq(vq, "Indirect within indirect"); + + /* + * Proposed update VIRTIO-134 spells this out: + * + * A driver MUST NOT set both VIRTQ_DESC_F_INDIRECT + * and VIRTQ_DESC_F_NEXT in flags. + */ + if (desc[i].flags & VRING_DESC_F_NEXT) + bad_driver_vq(vq, "indirect and next together"); + + if (desc[i].len % sizeof(struct vring_desc)) + bad_driver_vq(vq, + "Invalid size for indirect table"); + /* + * 2.4.5.3.2: + * + * The device MUST ignore the write-only flag + * (flags&VIRTQ_DESC_F_WRITE) in the descriptor that + * refers to an indirect table. + * + * We ignore it here: :) + */ + + max = desc[i].len / sizeof(struct vring_desc); + desc = check_pointer(vq->dev, desc[i].addr, desc[i].len); + i = 0; + + /* 2.4.5.3.1: + * + * A driver MUST NOT create a descriptor chain longer + * than the Queue Size of the device. + */ + if (max > vq->pci_config.queue_size) + bad_driver_vq(vq, + "indirect has too many entries"); + } + + /* Grab the first descriptor, and check it's OK. */ + iov[*out_num + *in_num].iov_len = desc[i].len; + iov[*out_num + *in_num].iov_base + = check_pointer(vq->dev, desc[i].addr, desc[i].len); + /* If this is an input descriptor, increment that count. */ + if (desc[i].flags & VRING_DESC_F_WRITE) + (*in_num)++; + else { + /* + * If it's an output descriptor, they're all supposed + * to come before any input descriptors. + */ + if (*in_num) + bad_driver_vq(vq, + "Descriptor has out after in"); + (*out_num)++; + } + + /* If we've got too many, that implies a descriptor loop. */ + if (*out_num + *in_num > max) + bad_driver_vq(vq, "Looped descriptor"); + } while ((i = next_desc(vq->dev, desc, i, max)) != max); + + return head; +} + +/* + * After we've used one of their buffers, we tell the Guest about it. Sometime + * later we'll want to send them an interrupt using trigger_irq(); note that + * wait_for_vq_desc() does that for us if it has to wait. + */ +static void add_used(struct virtqueue *vq, unsigned int head, int len) +{ + struct vring_used_elem *used; + + /* + * The virtqueue contains a ring of used buffers. Get a pointer to the + * next entry in that used ring. + */ + used = &vq->vring.used->ring[vq->vring.used->idx % vq->vring.num]; + used->id = head; + used->len = len; + /* Make sure buffer is written before we update index. */ + wmb(); + vq->vring.used->idx++; + vq->pending_used++; +} + +/* And here's the combo meal deal. Supersize me! */ +static void add_used_and_trigger(struct virtqueue *vq, unsigned head, int len) +{ + add_used(vq, head, len); + trigger_irq(vq); +} + +/* + * The Console + * + * We associate some data with the console for our exit hack. + */ +struct console_abort { + /* How many times have they hit ^C? */ + int count; + /* When did they start? */ + struct timeval start; +}; + +/* This is the routine which handles console input (ie. stdin). */ +static void console_input(struct virtqueue *vq) +{ + int len; + unsigned int head, in_num, out_num; + struct console_abort *abort = vq->dev->priv; + struct iovec iov[vq->vring.num]; + + /* Make sure there's a descriptor available. */ + head = wait_for_vq_desc(vq, iov, &out_num, &in_num); + if (out_num) + bad_driver_vq(vq, "Output buffers in console in queue?"); + + /* Read into it. This is where we usually wait. */ + len = readv(STDIN_FILENO, iov, in_num); + if (len <= 0) { + /* Ran out of input? */ + warnx("Failed to get console input, ignoring console."); + /* + * For simplicity, dying threads kill the whole Launcher. So + * just nap here. + */ + for (;;) + pause(); + } + + /* Tell the Guest we used a buffer. */ + add_used_and_trigger(vq, head, len); + + /* + * Three ^C within one second? Exit. + * + * This is such a hack, but works surprisingly well. Each ^C has to + * be in a buffer by itself, so they can't be too fast. But we check + * that we get three within about a second, so they can't be too + * slow. + */ + if (len != 1 || ((char *)iov[0].iov_base)[0] != 3) { + abort->count = 0; + return; + } + + abort->count++; + if (abort->count == 1) + gettimeofday(&abort->start, NULL); + else if (abort->count == 3) { + struct timeval now; + gettimeofday(&now, NULL); + /* Kill all Launcher processes with SIGINT, like normal ^C */ + if (now.tv_sec <= abort->start.tv_sec+1) + kill(0, SIGINT); + abort->count = 0; + } +} + +/* This is the routine which handles console output (ie. stdout). */ +static void console_output(struct virtqueue *vq) +{ + unsigned int head, out, in; + struct iovec iov[vq->vring.num]; + + /* We usually wait in here, for the Guest to give us something. */ + head = wait_for_vq_desc(vq, iov, &out, &in); + if (in) + bad_driver_vq(vq, "Input buffers in console output queue?"); + + /* writev can return a partial write, so we loop here. */ + while (!iov_empty(iov, out)) { + int len = writev(STDOUT_FILENO, iov, out); + if (len <= 0) { + warn("Write to stdout gave %i (%d)", len, errno); + break; + } + iov_consume(vq->dev, iov, out, NULL, len); + } + + /* + * We're finished with that buffer: if we're going to sleep, + * wait_for_vq_desc() will prod the Guest with an interrupt. + */ + add_used(vq, head, 0); +} + +/* + * The Network + * + * Handling output for network is also simple: we get all the output buffers + * and write them to /dev/net/tun. + */ +struct net_info { + int tunfd; +}; + +static void net_output(struct virtqueue *vq) +{ + struct net_info *net_info = vq->dev->priv; + unsigned int head, out, in; + struct iovec iov[vq->vring.num]; + + /* We usually wait in here for the Guest to give us a packet. */ + head = wait_for_vq_desc(vq, iov, &out, &in); + if (in) + bad_driver_vq(vq, "Input buffers in net output queue?"); + /* + * Send the whole thing through to /dev/net/tun. It expects the exact + * same format: what a coincidence! + */ + if (writev(net_info->tunfd, iov, out) < 0) + warnx("Write to tun failed (%d)?", errno); + + /* + * Done with that one; wait_for_vq_desc() will send the interrupt if + * all packets are processed. + */ + add_used(vq, head, 0); +} + +/* + * Handling network input is a bit trickier, because I've tried to optimize it. + * + * First we have a helper routine which tells is if from this file descriptor + * (ie. the /dev/net/tun device) will block: + */ +static bool will_block(int fd) +{ + fd_set fdset; + struct timeval zero = { 0, 0 }; + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + return select(fd+1, &fdset, NULL, NULL, &zero) != 1; +} + +/* + * This handles packets coming in from the tun device to our Guest. Like all + * service routines, it gets called again as soon as it returns, so you don't + * see a while(1) loop here. + */ +static void net_input(struct virtqueue *vq) +{ + int len; + unsigned int head, out, in; + struct iovec iov[vq->vring.num]; + struct net_info *net_info = vq->dev->priv; + + /* + * Get a descriptor to write an incoming packet into. This will also + * send an interrupt if they're out of descriptors. + */ + head = wait_for_vq_desc(vq, iov, &out, &in); + if (out) + bad_driver_vq(vq, "Output buffers in net input queue?"); + + /* + * If it looks like we'll block reading from the tun device, send them + * an interrupt. + */ + if (vq->pending_used && will_block(net_info->tunfd)) + trigger_irq(vq); + + /* + * Read in the packet. This is where we normally wait (when there's no + * incoming network traffic). + */ + len = readv(net_info->tunfd, iov, in); + if (len <= 0) + warn("Failed to read from tun (%d).", errno); + + /* + * Mark that packet buffer as used, but don't interrupt here. We want + * to wait until we've done as much work as we can. + */ + add_used(vq, head, len); +} +/*:*/ + +/* This is the helper to create threads: run the service routine in a loop. */ +static int do_thread(void *_vq) +{ + struct virtqueue *vq = _vq; + + for (;;) + vq->service(vq); + return 0; +} + +/* + * When a child dies, we kill our entire process group with SIGTERM. This + * also has the side effect that the shell restores the console for us! + */ +static void kill_launcher(int signal) +{ + kill(0, SIGTERM); +} + +static void reset_vq_pci_config(struct virtqueue *vq) +{ + vq->pci_config.queue_size = VIRTQUEUE_NUM; + vq->pci_config.queue_enable = 0; +} + +static void reset_device(struct device *dev) +{ + struct virtqueue *vq; + + verbose("Resetting device %s\n", dev->name); + + /* Clear any features they've acked. */ + dev->features_accepted = 0; + + /* We're going to be explicitly killing threads, so ignore them. */ + signal(SIGCHLD, SIG_IGN); + + /* + * 4.1.4.3.1: + * + * The device MUST present a 0 in queue_enable on reset. + * + * This means we set it here, and reset the saved ones in every vq. + */ + dev->mmio->cfg.queue_enable = 0; + + /* Get rid of the virtqueue threads */ + for (vq = dev->vq; vq; vq = vq->next) { + vq->last_avail_idx = 0; + reset_vq_pci_config(vq); + if (vq->thread != (pid_t)-1) { + kill(vq->thread, SIGTERM); + waitpid(vq->thread, NULL, 0); + vq->thread = (pid_t)-1; + } + } + dev->running = false; + dev->wrote_features_ok = false; + + /* Now we care if threads die. */ + signal(SIGCHLD, (void *)kill_launcher); +} + +static void cleanup_devices(void) +{ + unsigned int i; + + for (i = 1; i < MAX_PCI_DEVICES; i++) { + struct device *d = devices.pci[i]; + if (!d) + continue; + reset_device(d); + } + + /* If we saved off the original terminal settings, restore them now. */ + if (orig_term.c_lflag & (ISIG|ICANON|ECHO)) + tcsetattr(STDIN_FILENO, TCSANOW, &orig_term); +} + +/*L:217 + * We do PCI. This is mainly done to let us test the kernel virtio PCI + * code. + */ + +/* Linux expects a PCI host bridge: ours is a dummy, and first on the bus. */ +static struct device pci_host_bridge; + +static void init_pci_host_bridge(void) +{ + pci_host_bridge.name = "PCI Host Bridge"; + pci_host_bridge.config.class = 0x06; /* bridge */ + pci_host_bridge.config.subclass = 0; /* host bridge */ + devices.pci[0] = &pci_host_bridge; +} + +/* The IO ports used to read the PCI config space. */ +#define PCI_CONFIG_ADDR 0xCF8 +#define PCI_CONFIG_DATA 0xCFC + +/* + * Not really portable, but does help readability: this is what the Guest + * writes to the PCI_CONFIG_ADDR IO port. + */ +union pci_config_addr { + struct { + unsigned mbz: 2; + unsigned offset: 6; + unsigned funcnum: 3; + unsigned devnum: 5; + unsigned busnum: 8; + unsigned reserved: 7; + unsigned enabled : 1; + } bits; + u32 val; +}; + +/* + * We cache what they wrote to the address port, so we know what they're + * talking about when they access the data port. + */ +static union pci_config_addr pci_config_addr; + +static struct device *find_pci_device(unsigned int index) +{ + return devices.pci[index]; +} + +/* PCI can do 1, 2 and 4 byte reads; we handle that here. */ +static void ioread(u16 off, u32 v, u32 mask, u32 *val) +{ + assert(off < 4); + assert(mask == 0xFF || mask == 0xFFFF || mask == 0xFFFFFFFF); + *val = (v >> (off * 8)) & mask; +} + +/* PCI can do 1, 2 and 4 byte writes; we handle that here. */ +static void iowrite(u16 off, u32 v, u32 mask, u32 *dst) +{ + assert(off < 4); + assert(mask == 0xFF || mask == 0xFFFF || mask == 0xFFFFFFFF); + *dst &= ~(mask << (off * 8)); + *dst |= (v & mask) << (off * 8); +} + +/* + * Where PCI_CONFIG_DATA accesses depends on the previous write to + * PCI_CONFIG_ADDR. + */ +static struct device *dev_and_reg(u32 *reg) +{ + if (!pci_config_addr.bits.enabled) + return NULL; + + if (pci_config_addr.bits.funcnum != 0) + return NULL; + + if (pci_config_addr.bits.busnum != 0) + return NULL; + + if (pci_config_addr.bits.offset * 4 >= sizeof(struct pci_config)) + return NULL; + + *reg = pci_config_addr.bits.offset; + return find_pci_device(pci_config_addr.bits.devnum); +} + +/* + * We can get invalid combinations of values while they're writing, so we + * only fault if they try to write with some invalid bar/offset/length. + */ +static bool valid_bar_access(struct device *d, + struct virtio_pci_cfg_cap *cfg_access) +{ + /* We only have 1 bar (BAR0) */ + if (cfg_access->cap.bar != 0) + return false; + + /* Check it's within BAR0. */ + if (cfg_access->cap.offset >= d->mmio_size + || cfg_access->cap.offset + cfg_access->cap.length > d->mmio_size) + return false; + + /* Check length is 1, 2 or 4. */ + if (cfg_access->cap.length != 1 + && cfg_access->cap.length != 2 + && cfg_access->cap.length != 4) + return false; + + /* + * 4.1.4.7.2: + * + * The driver MUST NOT write a cap.offset which is not a multiple of + * cap.length (ie. all accesses MUST be aligned). + */ + if (cfg_access->cap.offset % cfg_access->cap.length != 0) + return false; + + /* Return pointer into word in BAR0. */ + return true; +} + +/* Is this accessing the PCI config address port?. */ +static bool is_pci_addr_port(u16 port) +{ + return port >= PCI_CONFIG_ADDR && port < PCI_CONFIG_ADDR + 4; +} + +static bool pci_addr_iowrite(u16 port, u32 mask, u32 val) +{ + iowrite(port - PCI_CONFIG_ADDR, val, mask, + &pci_config_addr.val); + verbose("PCI%s: %#x/%x: bus %u dev %u func %u reg %u\n", + pci_config_addr.bits.enabled ? "" : " DISABLED", + val, mask, + pci_config_addr.bits.busnum, + pci_config_addr.bits.devnum, + pci_config_addr.bits.funcnum, + pci_config_addr.bits.offset); + return true; +} + +static void pci_addr_ioread(u16 port, u32 mask, u32 *val) +{ + ioread(port - PCI_CONFIG_ADDR, pci_config_addr.val, mask, val); +} + +/* Is this accessing the PCI config data port?. */ +static bool is_pci_data_port(u16 port) +{ + return port >= PCI_CONFIG_DATA && port < PCI_CONFIG_DATA + 4; +} + +static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask); + +static bool pci_data_iowrite(u16 port, u32 mask, u32 val) +{ + u32 reg, portoff; + struct device *d = dev_and_reg(®); + + /* Complain if they don't belong to a device. */ + if (!d) + return false; + + /* They can do 1 byte writes, etc. */ + portoff = port - PCI_CONFIG_DATA; + + /* + * PCI uses a weird way to determine the BAR size: the OS + * writes all 1's, and sees which ones stick. + */ + if (&d->config_words[reg] == &d->config.bar[0]) { + int i; + + iowrite(portoff, val, mask, &d->config.bar[0]); + for (i = 0; (1 << i) < d->mmio_size; i++) + d->config.bar[0] &= ~(1 << i); + return true; + } else if ((&d->config_words[reg] > &d->config.bar[0] + && &d->config_words[reg] <= &d->config.bar[6]) + || &d->config_words[reg] == &d->config.expansion_rom_addr) { + /* Allow writing to any other BAR, or expansion ROM */ + iowrite(portoff, val, mask, &d->config_words[reg]); + return true; + /* We let them overide latency timer and cacheline size */ + } else if (&d->config_words[reg] == (void *)&d->config.cacheline_size) { + /* Only let them change the first two fields. */ + if (mask == 0xFFFFFFFF) + mask = 0xFFFF; + iowrite(portoff, val, mask, &d->config_words[reg]); + return true; + } else if (&d->config_words[reg] == (void *)&d->config.command + && mask == 0xFFFF) { + /* Ignore command writes. */ + return true; + } else if (&d->config_words[reg] + == (void *)&d->config.cfg_access.cap.bar + || &d->config_words[reg] + == &d->config.cfg_access.cap.length + || &d->config_words[reg] + == &d->config.cfg_access.cap.offset) { + + /* + * The VIRTIO_PCI_CAP_PCI_CFG capability + * provides a backdoor to access the MMIO + * regions without mapping them. Weird, but + * useful. + */ + iowrite(portoff, val, mask, &d->config_words[reg]); + return true; + } else if (&d->config_words[reg] == &d->config.cfg_access.pci_cfg_data) { + u32 write_mask; + + /* + * 4.1.4.7.1: + * + * Upon detecting driver write access to pci_cfg_data, the + * device MUST execute a write access at offset cap.offset at + * BAR selected by cap.bar using the first cap.length bytes + * from pci_cfg_data. + */ + + /* Must be bar 0 */ + if (!valid_bar_access(d, &d->config.cfg_access)) + return false; + + iowrite(portoff, val, mask, &d->config.cfg_access.pci_cfg_data); + + /* + * Now emulate a write. The mask we use is set by + * len, *not* this write! + */ + write_mask = (1ULL<<(8*d->config.cfg_access.cap.length)) - 1; + verbose("Window writing %#x/%#x to bar %u, offset %u len %u\n", + d->config.cfg_access.pci_cfg_data, write_mask, + d->config.cfg_access.cap.bar, + d->config.cfg_access.cap.offset, + d->config.cfg_access.cap.length); + + emulate_mmio_write(d, d->config.cfg_access.cap.offset, + d->config.cfg_access.pci_cfg_data, + write_mask); + return true; + } + + /* + * 4.1.4.1: + * + * The driver MUST NOT write into any field of the capability + * structure, with the exception of those with cap_type + * VIRTIO_PCI_CAP_PCI_CFG... + */ + return false; +} + +static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask); + +static void pci_data_ioread(u16 port, u32 mask, u32 *val) +{ + u32 reg; + struct device *d = dev_and_reg(®); + + if (!d) + return; + + /* Read through the PCI MMIO access window is special */ + if (&d->config_words[reg] == &d->config.cfg_access.pci_cfg_data) { + u32 read_mask; + + /* + * 4.1.4.7.1: + * + * Upon detecting driver read access to pci_cfg_data, the + * device MUST execute a read access of length cap.length at + * offset cap.offset at BAR selected by cap.bar and store the + * first cap.length bytes in pci_cfg_data. + */ + /* Must be bar 0 */ + if (!valid_bar_access(d, &d->config.cfg_access)) + bad_driver(d, + "Invalid cfg_access to bar%u, offset %u len %u", + d->config.cfg_access.cap.bar, + d->config.cfg_access.cap.offset, + d->config.cfg_access.cap.length); + + /* + * Read into the window. The mask we use is set by + * len, *not* this read! + */ + read_mask = (1ULL<<(8*d->config.cfg_access.cap.length))-1; + d->config.cfg_access.pci_cfg_data + = emulate_mmio_read(d, + d->config.cfg_access.cap.offset, + read_mask); + verbose("Window read %#x/%#x from bar %u, offset %u len %u\n", + d->config.cfg_access.pci_cfg_data, read_mask, + d->config.cfg_access.cap.bar, + d->config.cfg_access.cap.offset, + d->config.cfg_access.cap.length); + } + ioread(port - PCI_CONFIG_DATA, d->config_words[reg], mask, val); +} + +/*L:216 + * This is where we emulate a handful of Guest instructions. It's ugly + * and we used to do it in the kernel but it grew over time. + */ + +/* + * We use the ptrace syscall's pt_regs struct to talk about registers + * to lguest: these macros convert the names to the offsets. + */ +#define getreg(name) getreg_off(offsetof(struct user_regs_struct, name)) +#define setreg(name, val) \ + setreg_off(offsetof(struct user_regs_struct, name), (val)) + +static u32 getreg_off(size_t offset) +{ + u32 r; + unsigned long args[] = { LHREQ_GETREG, offset }; + + if (pwrite(lguest_fd, args, sizeof(args), cpu_id) < 0) + err(1, "Getting register %u", offset); + if (pread(lguest_fd, &r, sizeof(r), cpu_id) != sizeof(r)) + err(1, "Reading register %u", offset); + + return r; +} + +static void setreg_off(size_t offset, u32 val) +{ + unsigned long args[] = { LHREQ_SETREG, offset, val }; + + if (pwrite(lguest_fd, args, sizeof(args), cpu_id) < 0) + err(1, "Setting register %u", offset); +} + +/* Get register by instruction encoding */ +static u32 getreg_num(unsigned regnum, u32 mask) +{ + /* 8 bit ops use regnums 4-7 for high parts of word */ + if (mask == 0xFF && (regnum & 0x4)) + return getreg_num(regnum & 0x3, 0xFFFF) >> 8; + + switch (regnum) { + case 0: return getreg(eax) & mask; + case 1: return getreg(ecx) & mask; + case 2: return getreg(edx) & mask; + case 3: return getreg(ebx) & mask; + case 4: return getreg(esp) & mask; + case 5: return getreg(ebp) & mask; + case 6: return getreg(esi) & mask; + case 7: return getreg(edi) & mask; + } + abort(); +} + +/* Set register by instruction encoding */ +static void setreg_num(unsigned regnum, u32 val, u32 mask) +{ + /* Don't try to set bits out of range */ + assert(~(val & ~mask)); + + /* 8 bit ops use regnums 4-7 for high parts of word */ + if (mask == 0xFF && (regnum & 0x4)) { + /* Construct the 16 bits we want. */ + val = (val << 8) | getreg_num(regnum & 0x3, 0xFF); + setreg_num(regnum & 0x3, val, 0xFFFF); + return; + } + + switch (regnum) { + case 0: setreg(eax, val | (getreg(eax) & ~mask)); return; + case 1: setreg(ecx, val | (getreg(ecx) & ~mask)); return; + case 2: setreg(edx, val | (getreg(edx) & ~mask)); return; + case 3: setreg(ebx, val | (getreg(ebx) & ~mask)); return; + case 4: setreg(esp, val | (getreg(esp) & ~mask)); return; + case 5: setreg(ebp, val | (getreg(ebp) & ~mask)); return; + case 6: setreg(esi, val | (getreg(esi) & ~mask)); return; + case 7: setreg(edi, val | (getreg(edi) & ~mask)); return; + } + abort(); +} + +/* Get bytes of displacement appended to instruction, from r/m encoding */ +static u32 insn_displacement_len(u8 mod_reg_rm) +{ + /* Switch on the mod bits */ + switch (mod_reg_rm >> 6) { + case 0: + /* If mod == 0, and r/m == 101, 16-bit displacement follows */ + if ((mod_reg_rm & 0x7) == 0x5) + return 2; + /* Normally, mod == 0 means no literal displacement */ + return 0; + case 1: + /* One byte displacement */ + return 1; + case 2: + /* Four byte displacement */ + return 4; + case 3: + /* Register mode */ + return 0; + } + abort(); +} + +static void emulate_insn(const u8 insn[]) +{ + unsigned long args[] = { LHREQ_TRAP, 13 }; + unsigned int insnlen = 0, in = 0, small_operand = 0, byte_access; + unsigned int eax, port, mask; + /* + * Default is to return all-ones on IO port reads, which traditionally + * means "there's nothing there". + */ + u32 val = 0xFFFFFFFF; + + /* + * This must be the Guest kernel trying to do something, not userspace! + * The bottom two bits of the CS segment register are the privilege + * level. + */ + if ((getreg(xcs) & 3) != 0x1) + goto no_emulate; + + /* Decoding x86 instructions is icky. */ + + /* + * Around 2.6.33, the kernel started using an emulation for the + * cmpxchg8b instruction in early boot on many configurations. This + * code isn't paravirtualized, and it tries to disable interrupts. + * Ignore it, which will Mostly Work. + */ + if (insn[insnlen] == 0xfa) { + /* "cli", or Clear Interrupt Enable instruction. Skip it. */ + insnlen = 1; + goto skip_insn; + } + + /* + * 0x66 is an "operand prefix". It means a 16, not 32 bit in/out. + */ + if (insn[insnlen] == 0x66) { + small_operand = 1; + /* The instruction is 1 byte so far, read the next byte. */ + insnlen = 1; + } + + /* If the lower bit isn't set, it's a single byte access */ + byte_access = !(insn[insnlen] & 1); + + /* + * Now we can ignore the lower bit and decode the 4 opcodes + * we need to emulate. + */ + switch (insn[insnlen] & 0xFE) { + case 0xE4: /* in ,%al */ + port = insn[insnlen+1]; + insnlen += 2; + in = 1; + break; + case 0xEC: /* in (%dx),%al */ + port = getreg(edx) & 0xFFFF; + insnlen += 1; + in = 1; + break; + case 0xE6: /* out %al, */ + port = insn[insnlen+1]; + insnlen += 2; + break; + case 0xEE: /* out %al,(%dx) */ + port = getreg(edx) & 0xFFFF; + insnlen += 1; + break; + default: + /* OK, we don't know what this is, can't emulate. */ + goto no_emulate; + } + + /* Set a mask of the 1, 2 or 4 bytes, depending on size of IO */ + if (byte_access) + mask = 0xFF; + else if (small_operand) + mask = 0xFFFF; + else + mask = 0xFFFFFFFF; + + /* + * If it was an "IN" instruction, they expect the result to be read + * into %eax, so we change %eax. + */ + eax = getreg(eax); + + if (in) { + /* This is the PS/2 keyboard status; 1 means ready for output */ + if (port == 0x64) + val = 1; + else if (is_pci_addr_port(port)) + pci_addr_ioread(port, mask, &val); + else if (is_pci_data_port(port)) + pci_data_ioread(port, mask, &val); + + /* Clear the bits we're about to read */ + eax &= ~mask; + /* Copy bits in from val. */ + eax |= val & mask; + /* Now update the register. */ + setreg(eax, eax); + } else { + if (is_pci_addr_port(port)) { + if (!pci_addr_iowrite(port, mask, eax)) + goto bad_io; + } else if (is_pci_data_port(port)) { + if (!pci_data_iowrite(port, mask, eax)) + goto bad_io; + } + /* There are many other ports, eg. CMOS clock, serial + * and parallel ports, so we ignore them all. */ + } + + verbose("IO %s of %x to %u: %#08x\n", + in ? "IN" : "OUT", mask, port, eax); +skip_insn: + /* Finally, we've "done" the instruction, so move past it. */ + setreg(eip, getreg(eip) + insnlen); + return; + +bad_io: + warnx("Attempt to %s port %u (%#x mask)", + in ? "read from" : "write to", port, mask); + +no_emulate: + /* Inject trap into Guest. */ + if (write(lguest_fd, args, sizeof(args)) < 0) + err(1, "Reinjecting trap 13 for fault at %#x", getreg(eip)); +} + +static struct device *find_mmio_region(unsigned long paddr, u32 *off) +{ + unsigned int i; + + for (i = 1; i < MAX_PCI_DEVICES; i++) { + struct device *d = devices.pci[i]; + + if (!d) + continue; + if (paddr < d->mmio_addr) + continue; + if (paddr >= d->mmio_addr + d->mmio_size) + continue; + *off = paddr - d->mmio_addr; + return d; + } + return NULL; +} + +/* FIXME: Use vq array. */ +static struct virtqueue *vq_by_num(struct device *d, u32 num) +{ + struct virtqueue *vq = d->vq; + + while (num-- && vq) + vq = vq->next; + + return vq; +} + +static void save_vq_config(const struct virtio_pci_common_cfg *cfg, + struct virtqueue *vq) +{ + vq->pci_config = *cfg; +} + +static void restore_vq_config(struct virtio_pci_common_cfg *cfg, + struct virtqueue *vq) +{ + /* Only restore the per-vq part */ + size_t off = offsetof(struct virtio_pci_common_cfg, queue_size); + + memcpy((void *)cfg + off, (void *)&vq->pci_config + off, + sizeof(*cfg) - off); +} + +/* + * 4.1.4.3.2: + * + * The driver MUST configure the other virtqueue fields before + * enabling the virtqueue with queue_enable. + * + * When they enable the virtqueue, we check that their setup is valid. + */ +static void check_virtqueue(struct device *d, struct virtqueue *vq) +{ + /* Because lguest is 32 bit, all the descriptor high bits must be 0 */ + if (vq->pci_config.queue_desc_hi + || vq->pci_config.queue_avail_hi + || vq->pci_config.queue_used_hi) + bad_driver_vq(vq, "invalid 64-bit queue address"); + + /* + * 2.4.1: + * + * The driver MUST ensure that the physical address of the first byte + * of each virtqueue part is a multiple of the specified alignment + * value in the above table. + */ + if (vq->pci_config.queue_desc_lo % 16 + || vq->pci_config.queue_avail_lo % 2 + || vq->pci_config.queue_used_lo % 4) + bad_driver_vq(vq, "invalid alignment in queue addresses"); + + /* Initialize the virtqueue and check they're all in range. */ + vq->vring.num = vq->pci_config.queue_size; + vq->vring.desc = check_pointer(vq->dev, + vq->pci_config.queue_desc_lo, + sizeof(*vq->vring.desc) * vq->vring.num); + vq->vring.avail = check_pointer(vq->dev, + vq->pci_config.queue_avail_lo, + sizeof(*vq->vring.avail) + + (sizeof(vq->vring.avail->ring[0]) + * vq->vring.num)); + vq->vring.used = check_pointer(vq->dev, + vq->pci_config.queue_used_lo, + sizeof(*vq->vring.used) + + (sizeof(vq->vring.used->ring[0]) + * vq->vring.num)); + + /* + * 2.4.9.1: + * + * The driver MUST initialize flags in the used ring to 0 + * when allocating the used ring. + */ + if (vq->vring.used->flags != 0) + bad_driver_vq(vq, "invalid initial used.flags %#x", + vq->vring.used->flags); +} + +static void start_virtqueue(struct virtqueue *vq) +{ + /* + * Create stack for thread. Since the stack grows upwards, we point + * the stack pointer to the end of this region. + */ + char *stack = malloc(32768); + + /* Create a zero-initialized eventfd. */ + vq->eventfd = eventfd(0, 0); + if (vq->eventfd < 0) + err(1, "Creating eventfd"); + + /* + * CLONE_VM: because it has to access the Guest memory, and SIGCHLD so + * we get a signal if it dies. + */ + vq->thread = clone(do_thread, stack + 32768, CLONE_VM | SIGCHLD, vq); + if (vq->thread == (pid_t)-1) + err(1, "Creating clone"); +} + +static void start_virtqueues(struct device *d) +{ + struct virtqueue *vq; + + for (vq = d->vq; vq; vq = vq->next) { + if (vq->pci_config.queue_enable) + start_virtqueue(vq); + } +} + +static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) +{ + struct virtqueue *vq; + + switch (off) { + case offsetof(struct virtio_pci_mmio, cfg.device_feature_select): + /* + * 4.1.4.3.1: + * + * The device MUST present the feature bits it is offering in + * device_feature, starting at bit device_feature_select ∗ 32 + * for any device_feature_select written by the driver + */ + if (val == 0) + d->mmio->cfg.device_feature = d->features; + else if (val == 1) + d->mmio->cfg.device_feature = (d->features >> 32); + else + d->mmio->cfg.device_feature = 0; + goto feature_write_through32; + case offsetof(struct virtio_pci_mmio, cfg.guest_feature_select): + if (val > 1) + bad_driver(d, "Unexpected driver select %u", val); + goto feature_write_through32; + case offsetof(struct virtio_pci_mmio, cfg.guest_feature): + if (d->mmio->cfg.guest_feature_select == 0) { + d->features_accepted &= ~((u64)0xFFFFFFFF); + d->features_accepted |= val; + } else { + assert(d->mmio->cfg.guest_feature_select == 1); + d->features_accepted &= 0xFFFFFFFF; + d->features_accepted |= ((u64)val) << 32; + } + /* + * 2.2.1: + * + * The driver MUST NOT accept a feature which the device did + * not offer + */ + if (d->features_accepted & ~d->features) + bad_driver(d, "over-accepted features %#llx of %#llx", + d->features_accepted, d->features); + goto feature_write_through32; + case offsetof(struct virtio_pci_mmio, cfg.device_status): { + u8 prev; + + verbose("%s: device status -> %#x\n", d->name, val); + /* + * 4.1.4.3.1: + * + * The device MUST reset when 0 is written to device_status, + * and present a 0 in device_status once that is done. + */ + if (val == 0) { + reset_device(d); + goto write_through8; + } + + /* 2.1.1: The driver MUST NOT clear a device status bit. */ + if (d->mmio->cfg.device_status & ~val) + bad_driver(d, "unset of device status bit %#x -> %#x", + d->mmio->cfg.device_status, val); + + /* + * 2.1.2: + * + * The device MUST NOT consume buffers or notify the driver + * before DRIVER_OK. + */ + if (val & VIRTIO_CONFIG_S_DRIVER_OK + && !(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER_OK)) + start_virtqueues(d); + + /* + * 3.1.1: + * + * The driver MUST follow this sequence to initialize a device: + * - Reset the device. + * - Set the ACKNOWLEDGE status bit: the guest OS has + * notice the device. + * - Set the DRIVER status bit: the guest OS knows how + * to drive the device. + * - Read device feature bits, and write the subset + * of feature bits understood by the OS and driver + * to the device. During this step the driver MAY + * read (but MUST NOT write) the device-specific + * configuration fields to check that it can + * support the device before accepting it. + * - Set the FEATURES_OK status bit. The driver + * MUST not accept new feature bits after this + * step. + * - Re-read device status to ensure the FEATURES_OK + * bit is still set: otherwise, the device does + * not support our subset of features and the + * device is unusable. + * - Perform device-specific setup, including + * discovery of virtqueues for the device, + * optional per-bus setup, reading and possibly + * writing the device’s virtio configuration + * space, and population of virtqueues. + * - Set the DRIVER_OK status bit. At this point the + * device is “liveâ€. + */ + prev = 0; + switch (val & ~d->mmio->cfg.device_status) { + case VIRTIO_CONFIG_S_DRIVER_OK: + prev |= VIRTIO_CONFIG_S_FEATURES_OK; /* fall thru */ + case VIRTIO_CONFIG_S_FEATURES_OK: + prev |= VIRTIO_CONFIG_S_DRIVER; /* fall thru */ + case VIRTIO_CONFIG_S_DRIVER: + prev |= VIRTIO_CONFIG_S_ACKNOWLEDGE; /* fall thru */ + case VIRTIO_CONFIG_S_ACKNOWLEDGE: + break; + default: + bad_driver(d, "unknown device status bit %#x -> %#x", + d->mmio->cfg.device_status, val); + } + if (d->mmio->cfg.device_status != prev) + bad_driver(d, "unexpected status transition %#x -> %#x", + d->mmio->cfg.device_status, val); + + /* If they just wrote FEATURES_OK, we make sure they read */ + switch (val & ~d->mmio->cfg.device_status) { + case VIRTIO_CONFIG_S_FEATURES_OK: + d->wrote_features_ok = true; + break; + case VIRTIO_CONFIG_S_DRIVER_OK: + if (d->wrote_features_ok) + bad_driver(d, "did not re-read FEATURES_OK"); + break; + } + goto write_through8; + } + case offsetof(struct virtio_pci_mmio, cfg.queue_select): + vq = vq_by_num(d, val); + /* + * 4.1.4.3.1: + * + * The device MUST present a 0 in queue_size if the virtqueue + * corresponding to the current queue_select is unavailable. + */ + if (!vq) { + d->mmio->cfg.queue_size = 0; + goto write_through16; + } + /* Save registers for old vq, if it was a valid vq */ + if (d->mmio->cfg.queue_size) + save_vq_config(&d->mmio->cfg, + vq_by_num(d, d->mmio->cfg.queue_select)); + /* Restore the registers for the queue they asked for */ + restore_vq_config(&d->mmio->cfg, vq); + goto write_through16; + case offsetof(struct virtio_pci_mmio, cfg.queue_size): + /* + * 4.1.4.3.2: + * + * The driver MUST NOT write a value which is not a power of 2 + * to queue_size. + */ + if (val & (val-1)) + bad_driver(d, "invalid queue size %u", val); + if (d->mmio->cfg.queue_enable) + bad_driver(d, "changing queue size on live device"); + goto write_through16; + case offsetof(struct virtio_pci_mmio, cfg.queue_msix_vector): + bad_driver(d, "attempt to set MSIX vector to %u", val); + case offsetof(struct virtio_pci_mmio, cfg.queue_enable): { + struct virtqueue *vq = vq_by_num(d, d->mmio->cfg.queue_select); + + /* + * 4.1.4.3.2: + * + * The driver MUST NOT write a 0 to queue_enable. + */ + if (val != 1) + bad_driver(d, "setting queue_enable to %u", val); + + /* + * 3.1.1: + * + * 7. Perform device-specific setup, including discovery of + * virtqueues for the device, optional per-bus setup, + * reading and possibly writing the device’s virtio + * configuration space, and population of virtqueues. + * 8. Set the DRIVER_OK status bit. + * + * All our devices require all virtqueues to be enabled, so + * they should have done that before setting DRIVER_OK. + */ + if (d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER_OK) + bad_driver(d, "enabling vq after DRIVER_OK"); + + d->mmio->cfg.queue_enable = val; + save_vq_config(&d->mmio->cfg, vq); + check_virtqueue(d, vq); + goto write_through16; + } + case offsetof(struct virtio_pci_mmio, cfg.queue_notify_off): + bad_driver(d, "attempt to write to queue_notify_off"); + case offsetof(struct virtio_pci_mmio, cfg.queue_desc_lo): + case offsetof(struct virtio_pci_mmio, cfg.queue_desc_hi): + case offsetof(struct virtio_pci_mmio, cfg.queue_avail_lo): + case offsetof(struct virtio_pci_mmio, cfg.queue_avail_hi): + case offsetof(struct virtio_pci_mmio, cfg.queue_used_lo): + case offsetof(struct virtio_pci_mmio, cfg.queue_used_hi): + /* + * 4.1.4.3.2: + * + * The driver MUST configure the other virtqueue fields before + * enabling the virtqueue with queue_enable. + */ + if (d->mmio->cfg.queue_enable) + bad_driver(d, "changing queue on live device"); + + /* + * 3.1.1: + * + * The driver MUST follow this sequence to initialize a device: + *... + * 5. Set the FEATURES_OK status bit. The driver MUST not + * accept new feature bits after this step. + */ + if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_FEATURES_OK)) + bad_driver(d, "setting up vq before FEATURES_OK"); + + /* + * 6. Re-read device status to ensure the FEATURES_OK bit is + * still set... + */ + if (d->wrote_features_ok) + bad_driver(d, "didn't re-read FEATURES_OK before setup"); + + goto write_through32; + case offsetof(struct virtio_pci_mmio, notify): + vq = vq_by_num(d, val); + if (!vq) + bad_driver(d, "Invalid vq notification on %u", val); + /* Notify the process handling this vq by adding 1 to eventfd */ + write(vq->eventfd, "\1\0\0\0\0\0\0\0", 8); + goto write_through16; + case offsetof(struct virtio_pci_mmio, isr): + bad_driver(d, "Unexpected write to isr"); + /* Weird corner case: write to emerg_wr of console */ + case sizeof(struct virtio_pci_mmio) + + offsetof(struct virtio_console_config, emerg_wr): + if (strcmp(d->name, "console") == 0) { + char c = val; + write(STDOUT_FILENO, &c, 1); + goto write_through32; + } + /* Fall through... */ + default: + /* + * 4.1.4.3.2: + * + * The driver MUST NOT write to device_feature, num_queues, + * config_generation or queue_notify_off. + */ + bad_driver(d, "Unexpected write to offset %u", off); + } + +feature_write_through32: + /* + * 3.1.1: + * + * The driver MUST follow this sequence to initialize a device: + *... + * - Set the DRIVER status bit: the guest OS knows how + * to drive the device. + * - Read device feature bits, and write the subset + * of feature bits understood by the OS and driver + * to the device. + *... + * - Set the FEATURES_OK status bit. The driver MUST not + * accept new feature bits after this step. + */ + if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER)) + bad_driver(d, "feature write before VIRTIO_CONFIG_S_DRIVER"); + if (d->mmio->cfg.device_status & VIRTIO_CONFIG_S_FEATURES_OK) + bad_driver(d, "feature write after VIRTIO_CONFIG_S_FEATURES_OK"); + + /* + * 4.1.3.1: + * + * The driver MUST access each field using the “natural†access + * method, i.e. 32-bit accesses for 32-bit fields, 16-bit accesses for + * 16-bit fields and 8-bit accesses for 8-bit fields. + */ +write_through32: + if (mask != 0xFFFFFFFF) { + bad_driver(d, "non-32-bit write to offset %u (%#x)", + off, getreg(eip)); + return; + } + memcpy((char *)d->mmio + off, &val, 4); + return; + +write_through16: + if (mask != 0xFFFF) + bad_driver(d, "non-16-bit write to offset %u (%#x)", + off, getreg(eip)); + memcpy((char *)d->mmio + off, &val, 2); + return; + +write_through8: + if (mask != 0xFF) + bad_driver(d, "non-8-bit write to offset %u (%#x)", + off, getreg(eip)); + memcpy((char *)d->mmio + off, &val, 1); + return; +} + +static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) +{ + u8 isr; + u32 val = 0; + + switch (off) { + case offsetof(struct virtio_pci_mmio, cfg.device_feature_select): + case offsetof(struct virtio_pci_mmio, cfg.device_feature): + case offsetof(struct virtio_pci_mmio, cfg.guest_feature_select): + case offsetof(struct virtio_pci_mmio, cfg.guest_feature): + /* + * 3.1.1: + * + * The driver MUST follow this sequence to initialize a device: + *... + * - Set the DRIVER status bit: the guest OS knows how + * to drive the device. + * - Read device feature bits, and write the subset + * of feature bits understood by the OS and driver + * to the device. + */ + if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER)) + bad_driver(d, + "feature read before VIRTIO_CONFIG_S_DRIVER"); + goto read_through32; + case offsetof(struct virtio_pci_mmio, cfg.msix_config): + bad_driver(d, "read of msix_config"); + case offsetof(struct virtio_pci_mmio, cfg.num_queues): + goto read_through16; + case offsetof(struct virtio_pci_mmio, cfg.device_status): + /* As they did read, any write of FEATURES_OK is now fine. */ + d->wrote_features_ok = false; + goto read_through8; + case offsetof(struct virtio_pci_mmio, cfg.config_generation): + /* + * 4.1.4.3.1: + * + * The device MUST present a changed config_generation after + * the driver has read a device-specific configuration value + * which has changed since any part of the device-specific + * configuration was last read. + * + * This is simple: none of our devices change config, so this + * is always 0. + */ + goto read_through8; + case offsetof(struct virtio_pci_mmio, notify): + /* + * 3.1.1: + * + * The driver MUST NOT notify the device before setting + * DRIVER_OK. + */ + if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER_OK)) + bad_driver(d, "notify before VIRTIO_CONFIG_S_DRIVER_OK"); + goto read_through16; + case offsetof(struct virtio_pci_mmio, isr): + if (mask != 0xFF) + bad_driver(d, "non-8-bit read from offset %u (%#x)", + off, getreg(eip)); + isr = d->mmio->isr; + /* + * 4.1.4.5.1: + * + * The device MUST reset ISR status to 0 on driver read. + */ + d->mmio->isr = 0; + return isr; + case offsetof(struct virtio_pci_mmio, padding): + bad_driver(d, "read from padding (%#x)", getreg(eip)); + default: + /* Read from device config space, beware unaligned overflow */ + if (off > d->mmio_size - 4) + bad_driver(d, "read past end (%#x)", getreg(eip)); + + /* + * 3.1.1: + * The driver MUST follow this sequence to initialize a device: + *... + * 3. Set the DRIVER status bit: the guest OS knows how to + * drive the device. + * 4. Read device feature bits, and write the subset of + * feature bits understood by the OS and driver to the + * device. During this step the driver MAY read (but MUST NOT + * write) the device-specific configuration fields to check + * that it can support the device before accepting it. + */ + if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER)) + bad_driver(d, + "config read before VIRTIO_CONFIG_S_DRIVER"); + + if (mask == 0xFFFFFFFF) + goto read_through32; + else if (mask == 0xFFFF) + goto read_through16; + else + goto read_through8; + } + + /* + * 4.1.3.1: + * + * The driver MUST access each field using the “natural†access + * method, i.e. 32-bit accesses for 32-bit fields, 16-bit accesses for + * 16-bit fields and 8-bit accesses for 8-bit fields. + */ +read_through32: + if (mask != 0xFFFFFFFF) + bad_driver(d, "non-32-bit read to offset %u (%#x)", + off, getreg(eip)); + memcpy(&val, (char *)d->mmio + off, 4); + return val; + +read_through16: + if (mask != 0xFFFF) + bad_driver(d, "non-16-bit read to offset %u (%#x)", + off, getreg(eip)); + memcpy(&val, (char *)d->mmio + off, 2); + return val; + +read_through8: + if (mask != 0xFF) + bad_driver(d, "non-8-bit read to offset %u (%#x)", + off, getreg(eip)); + memcpy(&val, (char *)d->mmio + off, 1); + return val; +} + +static void emulate_mmio(unsigned long paddr, const u8 *insn) +{ + u32 val, off, mask = 0xFFFFFFFF, insnlen = 0; + struct device *d = find_mmio_region(paddr, &off); + unsigned long args[] = { LHREQ_TRAP, 14 }; + + if (!d) { + warnx("MMIO touching %#08lx (not a device)", paddr); + goto reinject; + } + + /* Prefix makes it a 16 bit op */ + if (insn[0] == 0x66) { + mask = 0xFFFF; + insnlen++; + } + + /* iowrite */ + if (insn[insnlen] == 0x89) { + /* Next byte is r/m byte: bits 3-5 are register. */ + val = getreg_num((insn[insnlen+1] >> 3) & 0x7, mask); + emulate_mmio_write(d, off, val, mask); + insnlen += 2 + insn_displacement_len(insn[insnlen+1]); + } else if (insn[insnlen] == 0x8b) { /* ioread */ + /* Next byte is r/m byte: bits 3-5 are register. */ + val = emulate_mmio_read(d, off, mask); + setreg_num((insn[insnlen+1] >> 3) & 0x7, val, mask); + insnlen += 2 + insn_displacement_len(insn[insnlen+1]); + } else if (insn[0] == 0x88) { /* 8-bit iowrite */ + mask = 0xff; + /* Next byte is r/m byte: bits 3-5 are register. */ + val = getreg_num((insn[1] >> 3) & 0x7, mask); + emulate_mmio_write(d, off, val, mask); + insnlen = 2 + insn_displacement_len(insn[1]); + } else if (insn[0] == 0x8a) { /* 8-bit ioread */ + mask = 0xff; + val = emulate_mmio_read(d, off, mask); + setreg_num((insn[1] >> 3) & 0x7, val, mask); + insnlen = 2 + insn_displacement_len(insn[1]); + } else { + warnx("Unknown MMIO instruction touching %#08lx:" + " %02x %02x %02x %02x at %u", + paddr, insn[0], insn[1], insn[2], insn[3], getreg(eip)); + reinject: + /* Inject trap into Guest. */ + if (write(lguest_fd, args, sizeof(args)) < 0) + err(1, "Reinjecting trap 14 for fault at %#x", + getreg(eip)); + return; + } + + /* Finally, we've "done" the instruction, so move past it. */ + setreg(eip, getreg(eip) + insnlen); +} + +/*L:190 + * Device Setup + * + * All devices need a descriptor so the Guest knows it exists, and a "struct + * device" so the Launcher can keep track of it. We have common helper + * routines to allocate and manage them. + */ +static void add_pci_virtqueue(struct device *dev, + void (*service)(struct virtqueue *), + const char *name) +{ + struct virtqueue **i, *vq = malloc(sizeof(*vq)); + + /* Initialize the virtqueue */ + vq->next = NULL; + vq->last_avail_idx = 0; + vq->dev = dev; + vq->name = name; + + /* + * This is the routine the service thread will run, and its Process ID + * once it's running. + */ + vq->service = service; + vq->thread = (pid_t)-1; + + /* Initialize the configuration. */ + reset_vq_pci_config(vq); + vq->pci_config.queue_notify_off = 0; + + /* Add one to the number of queues */ + vq->dev->mmio->cfg.num_queues++; + + /* + * Add to tail of list, so dev->vq is first vq, dev->vq->next is + * second. + */ + for (i = &dev->vq; *i; i = &(*i)->next); + *i = vq; +} + +/* The Guest accesses the feature bits via the PCI common config MMIO region */ +static void add_pci_feature(struct device *dev, unsigned bit) +{ + dev->features |= (1ULL << bit); +} + +/* For devices with no config. */ +static void no_device_config(struct device *dev) +{ + dev->mmio_addr = get_mmio_region(dev->mmio_size); + + dev->config.bar[0] = dev->mmio_addr; + /* Bottom 4 bits must be zero */ + assert(~(dev->config.bar[0] & 0xF)); +} + +/* This puts the device config into BAR0 */ +static void set_device_config(struct device *dev, const void *conf, size_t len) +{ + /* Set up BAR 0 */ + dev->mmio_size += len; + dev->mmio = realloc(dev->mmio, dev->mmio_size); + memcpy(dev->mmio + 1, conf, len); + + /* + * 4.1.4.6: + * + * The device MUST present at least one VIRTIO_PCI_CAP_DEVICE_CFG + * capability for any device type which has a device-specific + * configuration. + */ + /* Hook up device cfg */ + dev->config.cfg_access.cap.cap_next + = offsetof(struct pci_config, device); + + /* + * 4.1.4.6.1: + * + * The offset for the device-specific configuration MUST be 4-byte + * aligned. + */ + assert(dev->config.cfg_access.cap.cap_next % 4 == 0); + + /* Fix up device cfg field length. */ + dev->config.device.length = len; + + /* The rest is the same as the no-config case */ + no_device_config(dev); +} + +static void init_cap(struct virtio_pci_cap *cap, size_t caplen, int type, + size_t bar_offset, size_t bar_bytes, u8 next) +{ + cap->cap_vndr = PCI_CAP_ID_VNDR; + cap->cap_next = next; + cap->cap_len = caplen; + cap->cfg_type = type; + cap->bar = 0; + memset(cap->padding, 0, sizeof(cap->padding)); + cap->offset = bar_offset; + cap->length = bar_bytes; +} + +/* + * This sets up the pci_config structure, as defined in the virtio 1.0 + * standard (and PCI standard). + */ +static void init_pci_config(struct pci_config *pci, u16 type, + u8 class, u8 subclass) +{ + size_t bar_offset, bar_len; + + /* + * 4.1.4.4.1: + * + * The device MUST either present notify_off_multiplier as an even + * power of 2, or present notify_off_multiplier as 0. + * + * 2.1.2: + * + * The device MUST initialize device status to 0 upon reset. + */ + memset(pci, 0, sizeof(*pci)); + + /* 4.1.2.1: Devices MUST have the PCI Vendor ID 0x1AF4 */ + pci->vendor_id = 0x1AF4; + /* 4.1.2.1: ... PCI Device ID calculated by adding 0x1040 ... */ + pci->device_id = 0x1040 + type; + + /* + * PCI have specific codes for different types of devices. + * Linux doesn't care, but it's a good clue for people looking + * at the device. + */ + pci->class = class; + pci->subclass = subclass; + + /* + * 4.1.2.1: + * + * Non-transitional devices SHOULD have a PCI Revision ID of 1 or + * higher + */ + pci->revid = 1; + + /* + * 4.1.2.1: + * + * Non-transitional devices SHOULD have a PCI Subsystem Device ID of + * 0x40 or higher. + */ + pci->subsystem_device_id = 0x40; + + /* We use our dummy interrupt controller, and irq_line is the irq */ + pci->irq_line = devices.next_irq++; + pci->irq_pin = 0; + + /* Support for extended capabilities. */ + pci->status = (1 << 4); + + /* Link them in. */ + /* + * 4.1.4.3.1: + * + * The device MUST present at least one common configuration + * capability. + */ + pci->capabilities = offsetof(struct pci_config, common); + + /* 4.1.4.3.1 ... offset MUST be 4-byte aligned. */ + assert(pci->capabilities % 4 == 0); + + bar_offset = offsetof(struct virtio_pci_mmio, cfg); + bar_len = sizeof(((struct virtio_pci_mmio *)0)->cfg); + init_cap(&pci->common, sizeof(pci->common), VIRTIO_PCI_CAP_COMMON_CFG, + bar_offset, bar_len, + offsetof(struct pci_config, notify)); + + /* + * 4.1.4.4.1: + * + * The device MUST present at least one notification capability. + */ + bar_offset += bar_len; + bar_len = sizeof(((struct virtio_pci_mmio *)0)->notify); + + /* + * 4.1.4.4.1: + * + * The cap.offset MUST be 2-byte aligned. + */ + assert(pci->common.cap_next % 2 == 0); + + /* FIXME: Use a non-zero notify_off, for per-queue notification? */ + /* + * 4.1.4.4.1: + * + * The value cap.length presented by the device MUST be at least 2 and + * MUST be large enough to support queue notification offsets for all + * supported queues in all possible configurations. + */ + assert(bar_len >= 2); + + init_cap(&pci->notify.cap, sizeof(pci->notify), + VIRTIO_PCI_CAP_NOTIFY_CFG, + bar_offset, bar_len, + offsetof(struct pci_config, isr)); + + bar_offset += bar_len; + bar_len = sizeof(((struct virtio_pci_mmio *)0)->isr); + /* + * 4.1.4.5.1: + * + * The device MUST present at least one VIRTIO_PCI_CAP_ISR_CFG + * capability. + */ + init_cap(&pci->isr, sizeof(pci->isr), + VIRTIO_PCI_CAP_ISR_CFG, + bar_offset, bar_len, + offsetof(struct pci_config, cfg_access)); + + /* + * 4.1.4.7.1: + * + * The device MUST present at least one VIRTIO_PCI_CAP_PCI_CFG + * capability. + */ + /* This doesn't have any presence in the BAR */ + init_cap(&pci->cfg_access.cap, sizeof(pci->cfg_access), + VIRTIO_PCI_CAP_PCI_CFG, + 0, 0, 0); + + bar_offset += bar_len + sizeof(((struct virtio_pci_mmio *)0)->padding); + assert(bar_offset == sizeof(struct virtio_pci_mmio)); + + /* + * This gets sewn in and length set in set_device_config(). + * Some devices don't have a device configuration interface, so + * we never expose this if we don't call set_device_config(). + */ + init_cap(&pci->device, sizeof(pci->device), VIRTIO_PCI_CAP_DEVICE_CFG, + bar_offset, 0, 0); +} + +/* + * This routine does all the creation and setup of a new device, but we don't + * actually place the MMIO region until we know the size (if any) of the + * device-specific config. And we don't actually start the service threads + * until later. + * + * See what I mean about userspace being boring? + */ +static struct device *new_pci_device(const char *name, u16 type, + u8 class, u8 subclass) +{ + struct device *dev = malloc(sizeof(*dev)); + + /* Now we populate the fields one at a time. */ + dev->name = name; + dev->vq = NULL; + dev->running = false; + dev->wrote_features_ok = false; + dev->mmio_size = sizeof(struct virtio_pci_mmio); + dev->mmio = calloc(1, dev->mmio_size); + dev->features = (u64)1 << VIRTIO_F_VERSION_1; + dev->features_accepted = 0; + + if (devices.device_num + 1 >= MAX_PCI_DEVICES) + errx(1, "Can only handle 31 PCI devices"); + + init_pci_config(&dev->config, type, class, subclass); + assert(!devices.pci[devices.device_num+1]); + devices.pci[++devices.device_num] = dev; + + return dev; +} + +/* + * Our first setup routine is the console. It's a fairly simple device, but + * UNIX tty handling makes it uglier than it could be. + */ +static void setup_console(void) +{ + struct device *dev; + struct virtio_console_config conf; + + /* If we can save the initial standard input settings... */ + if (tcgetattr(STDIN_FILENO, &orig_term) == 0) { + struct termios term = orig_term; + /* + * Then we turn off echo, line buffering and ^C etc: We want a + * raw input stream to the Guest. + */ + term.c_lflag &= ~(ISIG|ICANON|ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &term); + } + + dev = new_pci_device("console", VIRTIO_ID_CONSOLE, 0x07, 0x00); + + /* We store the console state in dev->priv, and initialize it. */ + dev->priv = malloc(sizeof(struct console_abort)); + ((struct console_abort *)dev->priv)->count = 0; + + /* + * The console needs two virtqueues: the input then the output. When + * they put something the input queue, we make sure we're listening to + * stdin. When they put something in the output queue, we write it to + * stdout. + */ + add_pci_virtqueue(dev, console_input, "input"); + add_pci_virtqueue(dev, console_output, "output"); + + /* We need a configuration area for the emerg_wr early writes. */ + add_pci_feature(dev, VIRTIO_CONSOLE_F_EMERG_WRITE); + set_device_config(dev, &conf, sizeof(conf)); + + verbose("device %u: console\n", devices.device_num); +} +/*:*/ + +/*M:010 + * Inter-guest networking is an interesting area. Simplest is to have a + * --sharenet= option which opens or creates a named pipe. This can be + * used to send packets to another guest in a 1:1 manner. + * + * More sophisticated is to use one of the tools developed for project like UML + * to do networking. + * + * Faster is to do virtio bonding in kernel. Doing this 1:1 would be + * completely generic ("here's my vring, attach to your vring") and would work + * for any traffic. Of course, namespace and permissions issues need to be + * dealt with. A more sophisticated "multi-channel" virtio_net.c could hide + * multiple inter-guest channels behind one interface, although it would + * require some manner of hotplugging new virtio channels. + * + * Finally, we could use a virtio network switch in the kernel, ie. vhost. +:*/ + +static u32 str2ip(const char *ipaddr) +{ + unsigned int b[4]; + + if (sscanf(ipaddr, "%u.%u.%u.%u", &b[0], &b[1], &b[2], &b[3]) != 4) + errx(1, "Failed to parse IP address '%s'", ipaddr); + return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; +} + +static void str2mac(const char *macaddr, unsigned char mac[6]) +{ + unsigned int m[6]; + if (sscanf(macaddr, "%02x:%02x:%02x:%02x:%02x:%02x", + &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) != 6) + errx(1, "Failed to parse mac address '%s'", macaddr); + mac[0] = m[0]; + mac[1] = m[1]; + mac[2] = m[2]; + mac[3] = m[3]; + mac[4] = m[4]; + mac[5] = m[5]; +} + +/* + * This code is "adapted" from libbridge: it attaches the Host end of the + * network device to the bridge device specified by the command line. + * + * This is yet another James Morris contribution (I'm an IP-level guy, so I + * dislike bridging), and I just try not to break it. + */ +static void add_to_bridge(int fd, const char *if_name, const char *br_name) +{ + int ifidx; + struct ifreq ifr; + + if (!*br_name) + errx(1, "must specify bridge name"); + + ifidx = if_nametoindex(if_name); + if (!ifidx) + errx(1, "interface %s does not exist!", if_name); + + strncpy(ifr.ifr_name, br_name, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = '\0'; + ifr.ifr_ifindex = ifidx; + if (ioctl(fd, SIOCBRADDIF, &ifr) < 0) + err(1, "can't add %s to bridge %s", if_name, br_name); +} + +/* + * This sets up the Host end of the network device with an IP address, brings + * it up so packets will flow, the copies the MAC address into the hwaddr + * pointer. + */ +static void configure_device(int fd, const char *tapif, u32 ipaddr) +{ + struct ifreq ifr; + struct sockaddr_in sin; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, tapif); + + /* Don't read these incantations. Just cut & paste them like I did! */ + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(ipaddr); + memcpy(&ifr.ifr_addr, &sin, sizeof(sin)); + if (ioctl(fd, SIOCSIFADDR, &ifr) != 0) + err(1, "Setting %s interface address", tapif); + ifr.ifr_flags = IFF_UP; + if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) + err(1, "Bringing interface %s up", tapif); +} + +static int get_tun_device(char tapif[IFNAMSIZ]) +{ + struct ifreq ifr; + int vnet_hdr_sz; + int netfd; + + /* Start with this zeroed. Messy but sure. */ + memset(&ifr, 0, sizeof(ifr)); + + /* + * We open the /dev/net/tun device and tell it we want a tap device. A + * tap device is like a tun device, only somehow different. To tell + * the truth, I completely blundered my way through this code, but it + * works now! + */ + netfd = open_or_die("/dev/net/tun", O_RDWR); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR; + strcpy(ifr.ifr_name, "tap%d"); + if (ioctl(netfd, TUNSETIFF, &ifr) != 0) + err(1, "configuring /dev/net/tun"); + + if (ioctl(netfd, TUNSETOFFLOAD, + TUN_F_CSUM|TUN_F_TSO4|TUN_F_TSO6|TUN_F_TSO_ECN) != 0) + err(1, "Could not set features for tun device"); + + /* + * We don't need checksums calculated for packets coming in this + * device: trust us! + */ + ioctl(netfd, TUNSETNOCSUM, 1); + + /* + * In virtio before 1.0 (aka legacy virtio), we added a 16-bit + * field at the end of the network header iff + * VIRTIO_NET_F_MRG_RXBUF was negotiated. For virtio 1.0, + * that became the norm, but we need to tell the tun device + * about our expanded header (which is called + * virtio_net_hdr_mrg_rxbuf in the legacy system). + */ + vnet_hdr_sz = sizeof(struct virtio_net_hdr_v1); + if (ioctl(netfd, TUNSETVNETHDRSZ, &vnet_hdr_sz) != 0) + err(1, "Setting tun header size to %u", vnet_hdr_sz); + + memcpy(tapif, ifr.ifr_name, IFNAMSIZ); + return netfd; +} + +/*L:195 + * Our network is a Host<->Guest network. This can either use bridging or + * routing, but the principle is the same: it uses the "tun" device to inject + * packets into the Host as if they came in from a normal network card. We + * just shunt packets between the Guest and the tun device. + */ +static void setup_tun_net(char *arg) +{ + struct device *dev; + struct net_info *net_info = malloc(sizeof(*net_info)); + int ipfd; + u32 ip = INADDR_ANY; + bool bridging = false; + char tapif[IFNAMSIZ], *p; + struct virtio_net_config conf; + + net_info->tunfd = get_tun_device(tapif); + + /* First we create a new network device. */ + dev = new_pci_device("net", VIRTIO_ID_NET, 0x02, 0x00); + dev->priv = net_info; + + /* Network devices need a recv and a send queue, just like console. */ + add_pci_virtqueue(dev, net_input, "rx"); + add_pci_virtqueue(dev, net_output, "tx"); + + /* + * We need a socket to perform the magic network ioctls to bring up the + * tap interface, connect to the bridge etc. Any socket will do! + */ + ipfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (ipfd < 0) + err(1, "opening IP socket"); + + /* If the command line was --tunnet=bridge: do bridging. */ + if (!strncmp(BRIDGE_PFX, arg, strlen(BRIDGE_PFX))) { + arg += strlen(BRIDGE_PFX); + bridging = true; + } + + /* A mac address may follow the bridge name or IP address */ + p = strchr(arg, ':'); + if (p) { + str2mac(p+1, conf.mac); + add_pci_feature(dev, VIRTIO_NET_F_MAC); + *p = '\0'; + } + + /* arg is now either an IP address or a bridge name */ + if (bridging) + add_to_bridge(ipfd, tapif, arg); + else + ip = str2ip(arg); + + /* Set up the tun device. */ + configure_device(ipfd, tapif, ip); + + /* Expect Guest to handle everything except UFO */ + add_pci_feature(dev, VIRTIO_NET_F_CSUM); + add_pci_feature(dev, VIRTIO_NET_F_GUEST_CSUM); + add_pci_feature(dev, VIRTIO_NET_F_GUEST_TSO4); + add_pci_feature(dev, VIRTIO_NET_F_GUEST_TSO6); + add_pci_feature(dev, VIRTIO_NET_F_GUEST_ECN); + add_pci_feature(dev, VIRTIO_NET_F_HOST_TSO4); + add_pci_feature(dev, VIRTIO_NET_F_HOST_TSO6); + add_pci_feature(dev, VIRTIO_NET_F_HOST_ECN); + /* We handle indirect ring entries */ + add_pci_feature(dev, VIRTIO_RING_F_INDIRECT_DESC); + set_device_config(dev, &conf, sizeof(conf)); + + /* We don't need the socket any more; setup is done. */ + close(ipfd); + + if (bridging) + verbose("device %u: tun %s attached to bridge: %s\n", + devices.device_num, tapif, arg); + else + verbose("device %u: tun %s: %s\n", + devices.device_num, tapif, arg); +} +/*:*/ + +/* This hangs off device->priv. */ +struct vblk_info { + /* The size of the file. */ + off64_t len; + + /* The file descriptor for the file. */ + int fd; + +}; + +/*L:210 + * The Disk + * + * The disk only has one virtqueue, so it only has one thread. It is really + * simple: the Guest asks for a block number and we read or write that position + * in the file. + * + * Before we serviced each virtqueue in a separate thread, that was unacceptably + * slow: the Guest waits until the read is finished before running anything + * else, even if it could have been doing useful work. + * + * We could have used async I/O, except it's reputed to suck so hard that + * characters actually go missing from your code when you try to use it. + */ +static void blk_request(struct virtqueue *vq) +{ + struct vblk_info *vblk = vq->dev->priv; + unsigned int head, out_num, in_num, wlen; + int ret, i; + u8 *in; + struct virtio_blk_outhdr out; + struct iovec iov[vq->vring.num]; + off64_t off; + + /* + * Get the next request, where we normally wait. It triggers the + * interrupt to acknowledge previously serviced requests (if any). + */ + head = wait_for_vq_desc(vq, iov, &out_num, &in_num); + + /* Copy the output header from the front of the iov (adjusts iov) */ + iov_consume(vq->dev, iov, out_num, &out, sizeof(out)); + + /* Find and trim end of iov input array, for our status byte. */ + in = NULL; + for (i = out_num + in_num - 1; i >= out_num; i--) { + if (iov[i].iov_len > 0) { + in = iov[i].iov_base + iov[i].iov_len - 1; + iov[i].iov_len--; + break; + } + } + if (!in) + bad_driver_vq(vq, "Bad virtblk cmd with no room for status"); + + /* + * For historical reasons, block operations are expressed in 512 byte + * "sectors". + */ + off = out.sector * 512; + + if (out.type & VIRTIO_BLK_T_OUT) { + /* + * Write + * + * Move to the right location in the block file. This can fail + * if they try to write past end. + */ + if (lseek64(vblk->fd, off, SEEK_SET) != off) + err(1, "Bad seek to sector %llu", out.sector); + + ret = writev(vblk->fd, iov, out_num); + verbose("WRITE to sector %llu: %i\n", out.sector, ret); + + /* + * Grr... Now we know how long the descriptor they sent was, we + * make sure they didn't try to write over the end of the block + * file (possibly extending it). + */ + if (ret > 0 && off + ret > vblk->len) { + /* Trim it back to the correct length */ + ftruncate64(vblk->fd, vblk->len); + /* Die, bad Guest, die. */ + bad_driver_vq(vq, "Write past end %llu+%u", off, ret); + } + + wlen = sizeof(*in); + *in = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR); + } else if (out.type & VIRTIO_BLK_T_FLUSH) { + /* Flush */ + ret = fdatasync(vblk->fd); + verbose("FLUSH fdatasync: %i\n", ret); + wlen = sizeof(*in); + *in = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR); + } else { + /* + * Read + * + * Move to the right location in the block file. This can fail + * if they try to read past end. + */ + if (lseek64(vblk->fd, off, SEEK_SET) != off) + err(1, "Bad seek to sector %llu", out.sector); + + ret = readv(vblk->fd, iov + out_num, in_num); + if (ret >= 0) { + wlen = sizeof(*in) + ret; + *in = VIRTIO_BLK_S_OK; + } else { + wlen = sizeof(*in); + *in = VIRTIO_BLK_S_IOERR; + } + } + + /* Finished that request. */ + add_used(vq, head, wlen); +} + +/*L:198 This actually sets up a virtual block device. */ +static void setup_block_file(const char *filename) +{ + struct device *dev; + struct vblk_info *vblk; + struct virtio_blk_config conf; + + /* Create the device. */ + dev = new_pci_device("block", VIRTIO_ID_BLOCK, 0x01, 0x80); + + /* The device has one virtqueue, where the Guest places requests. */ + add_pci_virtqueue(dev, blk_request, "request"); + + /* Allocate the room for our own bookkeeping */ + vblk = dev->priv = malloc(sizeof(*vblk)); + + /* First we open the file and store the length. */ + vblk->fd = open_or_die(filename, O_RDWR|O_LARGEFILE); + vblk->len = lseek64(vblk->fd, 0, SEEK_END); + + /* Tell Guest how many sectors this device has. */ + conf.capacity = cpu_to_le64(vblk->len / 512); + + /* + * Tell Guest not to put in too many descriptors at once: two are used + * for the in and out elements. + */ + add_pci_feature(dev, VIRTIO_BLK_F_SEG_MAX); + conf.seg_max = cpu_to_le32(VIRTQUEUE_NUM - 2); + + set_device_config(dev, &conf, sizeof(struct virtio_blk_config)); + + verbose("device %u: virtblock %llu sectors\n", + devices.device_num, le64_to_cpu(conf.capacity)); +} + +/*L:211 + * Our random number generator device reads from /dev/urandom into the Guest's + * input buffers. The usual case is that the Guest doesn't want random numbers + * and so has no buffers although /dev/urandom is still readable, whereas + * console is the reverse. + * + * The same logic applies, however. + */ +struct rng_info { + int rfd; +}; + +static void rng_input(struct virtqueue *vq) +{ + int len; + unsigned int head, in_num, out_num, totlen = 0; + struct rng_info *rng_info = vq->dev->priv; + struct iovec iov[vq->vring.num]; + + /* First we need a buffer from the Guests's virtqueue. */ + head = wait_for_vq_desc(vq, iov, &out_num, &in_num); + if (out_num) + bad_driver_vq(vq, "Output buffers in rng?"); + + /* + * Just like the console write, we loop to cover the whole iovec. + * In this case, short reads actually happen quite a bit. + */ + while (!iov_empty(iov, in_num)) { + len = readv(rng_info->rfd, iov, in_num); + if (len <= 0) + err(1, "Read from /dev/urandom gave %i", len); + iov_consume(vq->dev, iov, in_num, NULL, len); + totlen += len; + } + + /* Tell the Guest about the new input. */ + add_used(vq, head, totlen); +} + +/*L:199 + * This creates a "hardware" random number device for the Guest. + */ +static void setup_rng(void) +{ + struct device *dev; + struct rng_info *rng_info = malloc(sizeof(*rng_info)); + + /* Our device's private info simply contains the /dev/urandom fd. */ + rng_info->rfd = open_or_die("/dev/urandom", O_RDONLY); + + /* Create the new device. */ + dev = new_pci_device("rng", VIRTIO_ID_RNG, 0xff, 0); + dev->priv = rng_info; + + /* The device has one virtqueue, where the Guest places inbufs. */ + add_pci_virtqueue(dev, rng_input, "input"); + + /* We don't have any configuration space */ + no_device_config(dev); + + verbose("device %u: rng\n", devices.device_num); +} +/* That's the end of device setup. */ + +/*L:230 Reboot is pretty easy: clean up and exec() the Launcher afresh. */ +static void __attribute__((noreturn)) restart_guest(void) +{ + unsigned int i; + + /* + * Since we don't track all open fds, we simply close everything beyond + * stderr. + */ + for (i = 3; i < FD_SETSIZE; i++) + close(i); + + /* Reset all the devices (kills all threads). */ + cleanup_devices(); + + execv(main_args[0], main_args); + err(1, "Could not exec %s", main_args[0]); +} + +/*L:220 + * Finally we reach the core of the Launcher which runs the Guest, serves + * its input and output, and finally, lays it to rest. + */ +static void __attribute__((noreturn)) run_guest(void) +{ + for (;;) { + struct lguest_pending notify; + int readval; + + /* We read from the /dev/lguest device to run the Guest. */ + readval = pread(lguest_fd, ¬ify, sizeof(notify), cpu_id); + if (readval == sizeof(notify)) { + if (notify.trap == 13) { + verbose("Emulating instruction at %#x\n", + getreg(eip)); + emulate_insn(notify.insn); + } else if (notify.trap == 14) { + verbose("Emulating MMIO at %#x\n", + getreg(eip)); + emulate_mmio(notify.addr, notify.insn); + } else + errx(1, "Unknown trap %i addr %#08x\n", + notify.trap, notify.addr); + /* ENOENT means the Guest died. Reading tells us why. */ + } else if (errno == ENOENT) { + char reason[1024] = { 0 }; + pread(lguest_fd, reason, sizeof(reason)-1, cpu_id); + errx(1, "%s", reason); + /* ERESTART means that we need to reboot the guest */ + } else if (errno == ERESTART) { + restart_guest(); + /* Anything else means a bug or incompatible change. */ + } else + err(1, "Running guest failed"); + } +} +/*L:240 + * This is the end of the Launcher. The good news: we are over halfway + * through! The bad news: the most fiendish part of the code still lies ahead + * of us. + * + * Are you ready? Take a deep breath and join me in the core of the Host, in + * "make Host". +:*/ + +static struct option opts[] = { + { "verbose", 0, NULL, 'v' }, + { "tunnet", 1, NULL, 't' }, + { "block", 1, NULL, 'b' }, + { "rng", 0, NULL, 'r' }, + { "initrd", 1, NULL, 'i' }, + { "username", 1, NULL, 'u' }, + { "chroot", 1, NULL, 'c' }, + { NULL }, +}; +static void usage(void) +{ + errx(1, "Usage: lguest [--verbose] " + "[--tunnet=(:|bridge::)\n" + "|--block=|--initrd=]...\n" + " vmlinux [args...]"); +} + +/*L:105 The main routine is where the real work begins: */ +int main(int argc, char *argv[]) +{ + /* Memory, code startpoint and size of the (optional) initrd. */ + unsigned long mem = 0, start, initrd_size = 0; + /* Two temporaries. */ + int i, c; + /* The boot information for the Guest. */ + struct boot_params *boot; + /* If they specify an initrd file to load. */ + const char *initrd_name = NULL; + + /* Password structure for initgroups/setres[gu]id */ + struct passwd *user_details = NULL; + + /* Directory to chroot to */ + char *chroot_path = NULL; + + /* Save the args: we "reboot" by execing ourselves again. */ + main_args = argv; + + /* + * First we initialize the device list. We remember next interrupt + * number to use for devices (1: remember that 0 is used by the timer). + */ + devices.next_irq = 1; + + /* We're CPU 0. In fact, that's the only CPU possible right now. */ + cpu_id = 0; + + /* + * We need to know how much memory so we can set up the device + * descriptor and memory pages for the devices as we parse the command + * line. So we quickly look through the arguments to find the amount + * of memory now. + */ + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-') { + mem = atoi(argv[i]) * 1024 * 1024; + /* + * We start by mapping anonymous pages over all of + * guest-physical memory range. This fills it with 0, + * and ensures that the Guest won't be killed when it + * tries to access it. + */ + guest_base = map_zeroed_pages(mem / getpagesize() + + DEVICE_PAGES); + guest_limit = mem; + guest_max = guest_mmio = mem + DEVICE_PAGES*getpagesize(); + break; + } + } + + /* We always have a console device, and it's always device 1. */ + setup_console(); + + /* The options are fairly straight-forward */ + while ((c = getopt_long(argc, argv, "v", opts, NULL)) != EOF) { + switch (c) { + case 'v': + verbose = true; + break; + case 't': + setup_tun_net(optarg); + break; + case 'b': + setup_block_file(optarg); + break; + case 'r': + setup_rng(); + break; + case 'i': + initrd_name = optarg; + break; + case 'u': + user_details = getpwnam(optarg); + if (!user_details) + err(1, "getpwnam failed, incorrect username?"); + break; + case 'c': + chroot_path = optarg; + break; + default: + warnx("Unknown argument %s", argv[optind]); + usage(); + } + } + /* + * After the other arguments we expect memory and kernel image name, + * followed by command line arguments for the kernel. + */ + if (optind + 2 > argc) + usage(); + + verbose("Guest base is at %p\n", guest_base); + + /* Initialize the (fake) PCI host bridge device. */ + init_pci_host_bridge(); + + /* Now we load the kernel */ + start = load_kernel(open_or_die(argv[optind+1], O_RDONLY)); + + /* Boot information is stashed at physical address 0 */ + boot = from_guest_phys(0); + + /* Map the initrd image if requested (at top of physical memory) */ + if (initrd_name) { + initrd_size = load_initrd(initrd_name, mem); + /* + * These are the location in the Linux boot header where the + * start and size of the initrd are expected to be found. + */ + boot->hdr.ramdisk_image = mem - initrd_size; + boot->hdr.ramdisk_size = initrd_size; + /* The bootloader type 0xFF means "unknown"; that's OK. */ + boot->hdr.type_of_loader = 0xFF; + } + + /* + * The Linux boot header contains an "E820" memory map: ours is a + * simple, single region. + */ + boot->e820_entries = 1; + boot->e820_map[0] = ((struct e820entry) { 0, mem, E820_RAM }); + /* + * The boot header contains a command line pointer: we put the command + * line after the boot header. + */ + boot->hdr.cmd_line_ptr = to_guest_phys(boot + 1); + /* We use a simple helper to copy the arguments separated by spaces. */ + concat((char *)(boot + 1), argv+optind+2); + + /* Set kernel alignment to 16M (CONFIG_PHYSICAL_ALIGN) */ + boot->hdr.kernel_alignment = 0x1000000; + + /* Boot protocol version: 2.07 supports the fields for lguest. */ + boot->hdr.version = 0x207; + + /* The hardware_subarch value of "1" tells the Guest it's an lguest. */ + boot->hdr.hardware_subarch = 1; + + /* Tell the entry path not to try to reload segment registers. */ + boot->hdr.loadflags |= KEEP_SEGMENTS; + + /* We tell the kernel to initialize the Guest. */ + tell_kernel(start); + + /* Ensure that we terminate if a device-servicing child dies. */ + signal(SIGCHLD, kill_launcher); + + /* If we exit via err(), this kills all the threads, restores tty. */ + atexit(cleanup_devices); + + /* If requested, chroot to a directory */ + if (chroot_path) { + if (chroot(chroot_path) != 0) + err(1, "chroot(\"%s\") failed", chroot_path); + + if (chdir("/") != 0) + err(1, "chdir(\"/\") failed"); + + verbose("chroot done\n"); + } + + /* If requested, drop privileges */ + if (user_details) { + uid_t u; + gid_t g; + + u = user_details->pw_uid; + g = user_details->pw_gid; + + if (initgroups(user_details->pw_name, g) != 0) + err(1, "initgroups failed"); + + if (setresgid(g, g, g) != 0) + err(1, "setresgid failed"); + + if (setresuid(u, u, u) != 0) + err(1, "setresuid failed"); + + verbose("Dropping privileges completed\n"); + } + + /* Finally, run the Guest. This doesn't return. */ + run_guest(); +} +/*:*/ + +/*M:999 + * Mastery is done: you now know everything I do. + * + * But surely you have seen code, features and bugs in your wanderings which + * you now yearn to attack? That is the real game, and I look forward to you + * patching and forking lguest into the Your-Name-Here-visor. + * + * Farewell, and good coding! + * Rusty Russell. + */ diff --git a/kernel/tools/lguest/lguest.txt b/kernel/tools/lguest/lguest.txt new file mode 100644 index 000000000..06e1f4649 --- /dev/null +++ b/kernel/tools/lguest/lguest.txt @@ -0,0 +1,125 @@ + __ + (___()'`; Rusty's Remarkably Unreliable Guide to Lguest + /, /` - or, A Young Coder's Illustrated Hypervisor + \\"--\\ http://lguest.ozlabs.org + +Lguest is designed to be a minimal 32-bit x86 hypervisor for the Linux kernel, +for Linux developers and users to experiment with virtualization with the +minimum of complexity. Nonetheless, it should have sufficient features to +make it useful for specific tasks, and, of course, you are encouraged to fork +and enhance it (see drivers/lguest/README). + +Features: + +- Kernel module which runs in a normal kernel. +- Simple I/O model for communication. +- Simple program to create new guests. +- Logo contains cute puppies: http://lguest.ozlabs.org + +Developer features: + +- Fun to hack on. +- No ABI: being tied to a specific kernel anyway, you can change anything. +- Many opportunities for improvement or feature implementation. + +Running Lguest: + +- The easiest way to run lguest is to use same kernel as guest and host. + You can configure them differently, but usually it's easiest not to. + + You will need to configure your kernel with the following options: + + "Processor type and features": + "Paravirtualized guest support" = Y + "Lguest guest support" = Y + "High Memory Support" = off/4GB + "Alignment value to which kernel should be aligned" = 0x100000 + (CONFIG_PARAVIRT=y, CONFIG_LGUEST_GUEST=y, CONFIG_HIGHMEM64G=n and + CONFIG_PHYSICAL_ALIGN=0x100000) + + "Device Drivers": + "Block devices" + "Virtio block driver" = M/Y + "Network device support" + "Universal TUN/TAP device driver support" = M/Y + "Virtio network driver" = M/Y + (CONFIG_VIRTIO_BLK=m, CONFIG_VIRTIO_NET=m and CONFIG_TUN=m) + + "Virtualization" + "Linux hypervisor example code" = M/Y + (CONFIG_LGUEST=m) + +- A tool called "lguest" is available in this directory: type "make" + to build it. If you didn't build your kernel in-tree, use "make + O=". + +- Create or find a root disk image. There are several useful ones + around, such as the xm-test tiny root image at + http://xm-test.xensource.com/ramdisks/initrd-1.1-i386.img + + For more serious work, I usually use a distribution ISO image and + install it under qemu, then make multiple copies: + + dd if=/dev/zero of=rootfile bs=1M count=2048 + qemu -cdrom image.iso -hda rootfile -net user -net nic -boot d + + Make sure that you install a getty on /dev/hvc0 if you want to log in on the + console! + +- "modprobe lg" if you built it as a module. + +- Run an lguest as root: + + tools/lguest/lguest 64 vmlinux --tunnet=192.168.19.1 \ + --block=rootfile root=/dev/vda + + Explanation: + 64: the amount of memory to use, in MB. + + vmlinux: the kernel image found in the top of your build directory. You + can also use a standard bzImage. + + --tunnet=192.168.19.1: configures a "tap" device for networking with this + IP address. + + --block=rootfile: a file or block device which becomes /dev/vda + inside the guest. + + root=/dev/vda: this (and anything else on the command line) are + kernel boot parameters. + +- Configuring networking. I usually have the host masquerade, using + "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE" and "echo 1 > + /proc/sys/net/ipv4/ip_forward". In this example, I would configure + eth0 inside the guest at 192.168.19.2. + + Another method is to bridge the tap device to an external interface + using --tunnet=bridge:, and perhaps run dhcp on the guest + to obtain an IP address. The bridge needs to be configured first: + this option simply adds the tap interface to it. + + A simple example on my system: + + ifconfig eth0 0.0.0.0 + brctl addbr lg0 + ifconfig lg0 up + brctl addif lg0 eth0 + dhclient lg0 + + Then use --tunnet=bridge:lg0 when launching the guest. + + See: + + http://www.linuxfoundation.org/collaborate/workgroups/networking/bridge + + for general information on how to get bridging to work. + +- Random number generation. Using the --rng option will provide a + /dev/hwrng in the guest that will read from the host's /dev/random. + Use this option in conjunction with rng-tools (see ../hw_random.txt) + to provide entropy to the guest kernel's /dev/random. + +There is a helpful mailing list at http://ozlabs.org/mailman/listinfo/lguest + +Good luck! +Rusty Russell rusty@rustcorp.com.au. diff --git a/kernel/tools/lib/api/Build b/kernel/tools/lib/api/Build new file mode 100644 index 000000000..3653965cf --- /dev/null +++ b/kernel/tools/lib/api/Build @@ -0,0 +1,2 @@ +libapi-y += fd/ +libapi-y += fs/ diff --git a/kernel/tools/lib/api/Makefile b/kernel/tools/lib/api/Makefile new file mode 100644 index 000000000..8bd960658 --- /dev/null +++ b/kernel/tools/lib/api/Makefile @@ -0,0 +1,43 @@ +include ../../scripts/Makefile.include +include ../../perf/config/utilities.mak # QUIET_CLEAN + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(shell pwd))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +#$(info Determined 'srctree' to be $(srctree)) +endif + +CC = $(CROSS_COMPILE)gcc +AR = $(CROSS_COMPILE)ar + +MAKEFLAGS += --no-print-directory + +LIBFILE = $(OUTPUT)libapi.a + +CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) +CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC +CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 + +RM = rm -f + +build := -f $(srctree)/tools/build/Makefile.build dir=. obj +API_IN := $(OUTPUT)libapi-in.o + +export srctree OUTPUT CC LD CFLAGS V + +all: $(LIBFILE) + +$(API_IN): FORCE + @$(MAKE) $(build)=libapi + +$(LIBFILE): $(API_IN) + $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(API_IN) + +clean: + $(call QUIET_CLEAN, libapi) $(RM) $(LIBFILE); \ + find $(if $(OUTPUT),$(OUTPUT),.) -name \*.o | xargs $(RM) + +FORCE: + +.PHONY: clean FORCE diff --git a/kernel/tools/lib/api/fd/Build b/kernel/tools/lib/api/fd/Build new file mode 100644 index 000000000..605d99f6d --- /dev/null +++ b/kernel/tools/lib/api/fd/Build @@ -0,0 +1 @@ +libapi-y += array.o diff --git a/kernel/tools/lib/api/fd/array.c b/kernel/tools/lib/api/fd/array.c new file mode 100644 index 000000000..0e636c433 --- /dev/null +++ b/kernel/tools/lib/api/fd/array.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2014, Red Hat Inc, Arnaldo Carvalho de Melo + * + * Released under the GPL v2. (and only v2, not any later version) + */ +#include "array.h" +#include +#include +#include +#include +#include + +void fdarray__init(struct fdarray *fda, int nr_autogrow) +{ + fda->entries = NULL; + fda->priv = NULL; + fda->nr = fda->nr_alloc = 0; + fda->nr_autogrow = nr_autogrow; +} + +int fdarray__grow(struct fdarray *fda, int nr) +{ + void *priv; + int nr_alloc = fda->nr_alloc + nr; + size_t psize = sizeof(fda->priv[0]) * nr_alloc; + size_t size = sizeof(struct pollfd) * nr_alloc; + struct pollfd *entries = realloc(fda->entries, size); + + if (entries == NULL) + return -ENOMEM; + + priv = realloc(fda->priv, psize); + if (priv == NULL) { + free(entries); + return -ENOMEM; + } + + fda->nr_alloc = nr_alloc; + fda->entries = entries; + fda->priv = priv; + return 0; +} + +struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow) +{ + struct fdarray *fda = calloc(1, sizeof(*fda)); + + if (fda != NULL) { + if (fdarray__grow(fda, nr_alloc)) { + free(fda); + fda = NULL; + } else { + fda->nr_autogrow = nr_autogrow; + } + } + + return fda; +} + +void fdarray__exit(struct fdarray *fda) +{ + free(fda->entries); + free(fda->priv); + fdarray__init(fda, 0); +} + +void fdarray__delete(struct fdarray *fda) +{ + fdarray__exit(fda); + free(fda); +} + +int fdarray__add(struct fdarray *fda, int fd, short revents) +{ + int pos = fda->nr; + + if (fda->nr == fda->nr_alloc && + fdarray__grow(fda, fda->nr_autogrow) < 0) + return -ENOMEM; + + fda->entries[fda->nr].fd = fd; + fda->entries[fda->nr].events = revents; + fda->nr++; + return pos; +} + +int fdarray__filter(struct fdarray *fda, short revents, + void (*entry_destructor)(struct fdarray *fda, int fd)) +{ + int fd, nr = 0; + + if (fda->nr == 0) + return 0; + + for (fd = 0; fd < fda->nr; ++fd) { + if (fda->entries[fd].revents & revents) { + if (entry_destructor) + entry_destructor(fda, fd); + + continue; + } + + if (fd != nr) { + fda->entries[nr] = fda->entries[fd]; + fda->priv[nr] = fda->priv[fd]; + } + + ++nr; + } + + return fda->nr = nr; +} + +int fdarray__poll(struct fdarray *fda, int timeout) +{ + return poll(fda->entries, fda->nr, timeout); +} + +int fdarray__fprintf(struct fdarray *fda, FILE *fp) +{ + int fd, printed = fprintf(fp, "%d [ ", fda->nr); + + for (fd = 0; fd < fda->nr; ++fd) + printed += fprintf(fp, "%s%d", fd ? ", " : "", fda->entries[fd].fd); + + return printed + fprintf(fp, " ]"); +} diff --git a/kernel/tools/lib/api/fd/array.h b/kernel/tools/lib/api/fd/array.h new file mode 100644 index 000000000..45db01818 --- /dev/null +++ b/kernel/tools/lib/api/fd/array.h @@ -0,0 +1,46 @@ +#ifndef __API_FD_ARRAY__ +#define __API_FD_ARRAY__ + +#include + +struct pollfd; + +/** + * struct fdarray: Array of file descriptors + * + * @priv: Per array entry priv area, users should access just its contents, + * not set it to anything, as it is kept in synch with @entries, being + * realloc'ed, * for instance, in fdarray__{grow,filter}. + * + * I.e. using 'fda->priv[N].idx = * value' where N < fda->nr is ok, + * but doing 'fda->priv = malloc(M)' is not allowed. + */ +struct fdarray { + int nr; + int nr_alloc; + int nr_autogrow; + struct pollfd *entries; + union { + int idx; + } *priv; +}; + +void fdarray__init(struct fdarray *fda, int nr_autogrow); +void fdarray__exit(struct fdarray *fda); + +struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow); +void fdarray__delete(struct fdarray *fda); + +int fdarray__add(struct fdarray *fda, int fd, short revents); +int fdarray__poll(struct fdarray *fda, int timeout); +int fdarray__filter(struct fdarray *fda, short revents, + void (*entry_destructor)(struct fdarray *fda, int fd)); +int fdarray__grow(struct fdarray *fda, int extra); +int fdarray__fprintf(struct fdarray *fda, FILE *fp); + +static inline int fdarray__available_entries(struct fdarray *fda) +{ + return fda->nr_alloc - fda->nr; +} + +#endif /* __API_FD_ARRAY__ */ diff --git a/kernel/tools/lib/api/fs/Build b/kernel/tools/lib/api/fs/Build new file mode 100644 index 000000000..6de5a4f0b --- /dev/null +++ b/kernel/tools/lib/api/fs/Build @@ -0,0 +1,4 @@ +libapi-y += fs.o +libapi-y += debugfs.o +libapi-y += findfs.o +libapi-y += tracefs.o diff --git a/kernel/tools/lib/api/fs/debugfs.c b/kernel/tools/lib/api/fs/debugfs.c new file mode 100644 index 000000000..8305b3e9d --- /dev/null +++ b/kernel/tools/lib/api/fs/debugfs.c @@ -0,0 +1,118 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debugfs.h" + +#ifndef DEBUGFS_DEFAULT_PATH +#define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug" +#endif + +char debugfs_mountpoint[PATH_MAX + 1] = DEBUGFS_DEFAULT_PATH; + +static const char * const debugfs_known_mountpoints[] = { + DEBUGFS_DEFAULT_PATH, + "/debug", + 0, +}; + +static bool debugfs_found; + +bool debugfs_configured(void) +{ + return debugfs_find_mountpoint() != NULL; +} + +/* find the path to the mounted debugfs */ +const char *debugfs_find_mountpoint(void) +{ + const char *ret; + + if (debugfs_found) + return (const char *)debugfs_mountpoint; + + ret = find_mountpoint("debugfs", (long) DEBUGFS_MAGIC, + debugfs_mountpoint, PATH_MAX + 1, + debugfs_known_mountpoints); + if (ret) + debugfs_found = true; + + return ret; +} + +/* mount the debugfs somewhere if it's not mounted */ +char *debugfs_mount(const char *mountpoint) +{ + /* see if it's already mounted */ + if (debugfs_find_mountpoint()) + goto out; + + /* if not mounted and no argument */ + if (mountpoint == NULL) { + /* see if environment variable set */ + mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT); + /* if no environment variable, use default */ + if (mountpoint == NULL) + mountpoint = DEBUGFS_DEFAULT_PATH; + } + + if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0) + return NULL; + + /* save the mountpoint */ + debugfs_found = true; + strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); +out: + return debugfs_mountpoint; +} + +int debugfs__strerror_open(int err, char *buf, size_t size, const char *filename) +{ + char sbuf[128]; + + switch (err) { + case ENOENT: + if (debugfs_found) { + snprintf(buf, size, + "Error:\tFile %s/%s not found.\n" + "Hint:\tPerhaps this kernel misses some CONFIG_ setting to enable this feature?.\n", + debugfs_mountpoint, filename); + break; + } + snprintf(buf, size, "%s", + "Error:\tUnable to find debugfs\n" + "Hint:\tWas your kernel compiled with debugfs support?\n" + "Hint:\tIs the debugfs filesystem mounted?\n" + "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'"); + break; + case EACCES: + snprintf(buf, size, + "Error:\tNo permissions to read %s/%s\n" + "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", + debugfs_mountpoint, filename, debugfs_mountpoint); + break; + default: + snprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf))); + break; + } + + return 0; +} + +int debugfs__strerror_open_tp(int err, char *buf, size_t size, const char *sys, const char *name) +{ + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "tracing/events/%s/%s", sys, name ?: "*"); + + return debugfs__strerror_open(err, buf, size, path); +} diff --git a/kernel/tools/lib/api/fs/debugfs.h b/kernel/tools/lib/api/fs/debugfs.h new file mode 100644 index 000000000..455023698 --- /dev/null +++ b/kernel/tools/lib/api/fs/debugfs.h @@ -0,0 +1,23 @@ +#ifndef __API_DEBUGFS_H__ +#define __API_DEBUGFS_H__ + +#include "findfs.h" + +#ifndef DEBUGFS_MAGIC +#define DEBUGFS_MAGIC 0x64626720 +#endif + +#ifndef PERF_DEBUGFS_ENVIRONMENT +#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR" +#endif + +bool debugfs_configured(void); +const char *debugfs_find_mountpoint(void); +char *debugfs_mount(const char *mountpoint); + +extern char debugfs_mountpoint[]; + +int debugfs__strerror_open(int err, char *buf, size_t size, const char *filename); +int debugfs__strerror_open_tp(int err, char *buf, size_t size, const char *sys, const char *name); + +#endif /* __API_DEBUGFS_H__ */ diff --git a/kernel/tools/lib/api/fs/findfs.c b/kernel/tools/lib/api/fs/findfs.c new file mode 100644 index 000000000..49946cb6d --- /dev/null +++ b/kernel/tools/lib/api/fs/findfs.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include + +#include "findfs.h" + +/* verify that a mountpoint is actually the type we want */ + +int valid_mountpoint(const char *mount, long magic) +{ + struct statfs st_fs; + + if (statfs(mount, &st_fs) < 0) + return -ENOENT; + else if ((long)st_fs.f_type != magic) + return -ENOENT; + + return 0; +} + +/* find the path to a mounted file system */ +const char *find_mountpoint(const char *fstype, long magic, + char *mountpoint, int len, + const char * const *known_mountpoints) +{ + const char * const *ptr; + char format[128]; + char type[100]; + FILE *fp; + + if (known_mountpoints) { + ptr = known_mountpoints; + while (*ptr) { + if (valid_mountpoint(*ptr, magic) == 0) { + strncpy(mountpoint, *ptr, len - 1); + mountpoint[len-1] = 0; + return mountpoint; + } + ptr++; + } + } + + /* give up and parse /proc/mounts */ + fp = fopen("/proc/mounts", "r"); + if (fp == NULL) + return NULL; + + snprintf(format, 128, "%%*s %%%ds %%99s %%*s %%*d %%*d\n", len); + + while (fscanf(fp, format, mountpoint, type) == 2) { + if (strcmp(type, fstype) == 0) + break; + } + fclose(fp); + + if (strcmp(type, fstype) != 0) + return NULL; + + return mountpoint; +} diff --git a/kernel/tools/lib/api/fs/findfs.h b/kernel/tools/lib/api/fs/findfs.h new file mode 100644 index 000000000..b6f5d05ac --- /dev/null +++ b/kernel/tools/lib/api/fs/findfs.h @@ -0,0 +1,23 @@ +#ifndef __API_FINDFS_H__ +#define __API_FINDFS_H__ + +#include + +#define _STR(x) #x +#define STR(x) _STR(x) + +/* + * On most systems would have given us this, but not on some systems + * (e.g. GNU/Hurd). + */ +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +const char *find_mountpoint(const char *fstype, long magic, + char *mountpoint, int len, + const char * const *known_mountpoints); + +int valid_mountpoint(const char *mount, long magic); + +#endif /* __API_FINDFS_H__ */ diff --git a/kernel/tools/lib/api/fs/fs.c b/kernel/tools/lib/api/fs/fs.c new file mode 100644 index 000000000..128ef6332 --- /dev/null +++ b/kernel/tools/lib/api/fs/fs.c @@ -0,0 +1,199 @@ +/* TODO merge/factor in debugfs.c here */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debugfs.h" +#include "fs.h" + +static const char * const sysfs__fs_known_mountpoints[] = { + "/sys", + 0, +}; + +static const char * const procfs__known_mountpoints[] = { + "/proc", + 0, +}; + +struct fs { + const char *name; + const char * const *mounts; + char path[PATH_MAX + 1]; + bool found; + long magic; +}; + +enum { + FS__SYSFS = 0, + FS__PROCFS = 1, +}; + +static struct fs fs__entries[] = { + [FS__SYSFS] = { + .name = "sysfs", + .mounts = sysfs__fs_known_mountpoints, + .magic = SYSFS_MAGIC, + }, + [FS__PROCFS] = { + .name = "proc", + .mounts = procfs__known_mountpoints, + .magic = PROC_SUPER_MAGIC, + }, +}; + +static bool fs__read_mounts(struct fs *fs) +{ + bool found = false; + char type[100]; + FILE *fp; + + fp = fopen("/proc/mounts", "r"); + if (fp == NULL) + return NULL; + + while (!found && + fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", + fs->path, type) == 2) { + + if (strcmp(type, fs->name) == 0) + found = true; + } + + fclose(fp); + return fs->found = found; +} + +static int fs__valid_mount(const char *fs, long magic) +{ + struct statfs st_fs; + + if (statfs(fs, &st_fs) < 0) + return -ENOENT; + else if ((long)st_fs.f_type != magic) + return -ENOENT; + + return 0; +} + +static bool fs__check_mounts(struct fs *fs) +{ + const char * const *ptr; + + ptr = fs->mounts; + while (*ptr) { + if (fs__valid_mount(*ptr, fs->magic) == 0) { + fs->found = true; + strcpy(fs->path, *ptr); + return true; + } + ptr++; + } + + return false; +} + +static void mem_toupper(char *f, size_t len) +{ + while (len) { + *f = toupper(*f); + f++; + len--; + } +} + +/* + * Check for "NAME_PATH" environment variable to override fs location (for + * testing). This matches the recommendation in Documentation/sysfs-rules.txt + * for SYSFS_PATH. + */ +static bool fs__env_override(struct fs *fs) +{ + char *override_path; + size_t name_len = strlen(fs->name); + /* name + "_PATH" + '\0' */ + char upper_name[name_len + 5 + 1]; + memcpy(upper_name, fs->name, name_len); + mem_toupper(upper_name, name_len); + strcpy(&upper_name[name_len], "_PATH"); + + override_path = getenv(upper_name); + if (!override_path) + return false; + + fs->found = true; + strncpy(fs->path, override_path, sizeof(fs->path)); + return true; +} + +static const char *fs__get_mountpoint(struct fs *fs) +{ + if (fs__env_override(fs)) + return fs->path; + + if (fs__check_mounts(fs)) + return fs->path; + + if (fs__read_mounts(fs)) + return fs->path; + + return NULL; +} + +static const char *fs__mountpoint(int idx) +{ + struct fs *fs = &fs__entries[idx]; + + if (fs->found) + return (const char *)fs->path; + + return fs__get_mountpoint(fs); +} + +#define FS__MOUNTPOINT(name, idx) \ +const char *name##__mountpoint(void) \ +{ \ + return fs__mountpoint(idx); \ +} + +FS__MOUNTPOINT(sysfs, FS__SYSFS); +FS__MOUNTPOINT(procfs, FS__PROCFS); + +int filename__read_int(const char *filename, int *value) +{ + char line[64]; + int fd = open(filename, O_RDONLY), err = -1; + + if (fd < 0) + return -1; + + if (read(fd, line, sizeof(line)) > 0) { + *value = atoi(line); + err = 0; + } + + close(fd); + return err; +} + +int sysctl__read_int(const char *sysctl, int *value) +{ + char path[PATH_MAX]; + const char *procfs = procfs__mountpoint(); + + if (!procfs) + return -1; + + snprintf(path, sizeof(path), "%s/sys/%s", procfs, sysctl); + + return filename__read_int(path, value); +} diff --git a/kernel/tools/lib/api/fs/fs.h b/kernel/tools/lib/api/fs/fs.h new file mode 100644 index 000000000..6caa2bbc6 --- /dev/null +++ b/kernel/tools/lib/api/fs/fs.h @@ -0,0 +1,17 @@ +#ifndef __API_FS__ +#define __API_FS__ + +#ifndef SYSFS_MAGIC +#define SYSFS_MAGIC 0x62656572 +#endif + +#ifndef PROC_SUPER_MAGIC +#define PROC_SUPER_MAGIC 0x9fa0 +#endif + +const char *sysfs__mountpoint(void); +const char *procfs__mountpoint(void); + +int filename__read_int(const char *filename, int *value); +int sysctl__read_int(const char *sysctl, int *value); +#endif /* __API_FS__ */ diff --git a/kernel/tools/lib/api/fs/tracefs.c b/kernel/tools/lib/api/fs/tracefs.c new file mode 100644 index 000000000..e4aa9688b --- /dev/null +++ b/kernel/tools/lib/api/fs/tracefs.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tracefs.h" + +#ifndef TRACEFS_DEFAULT_PATH +#define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing" +#endif + +char tracefs_mountpoint[PATH_MAX + 1] = TRACEFS_DEFAULT_PATH; + +static const char * const tracefs_known_mountpoints[] = { + TRACEFS_DEFAULT_PATH, + "/sys/kernel/debug/tracing", + "/tracing", + "/trace", + 0, +}; + +static bool tracefs_found; + +bool tracefs_configured(void) +{ + return tracefs_find_mountpoint() != NULL; +} + +/* find the path to the mounted tracefs */ +const char *tracefs_find_mountpoint(void) +{ + const char *ret; + + if (tracefs_found) + return (const char *)tracefs_mountpoint; + + ret = find_mountpoint("tracefs", (long) TRACEFS_MAGIC, + tracefs_mountpoint, PATH_MAX + 1, + tracefs_known_mountpoints); + + if (ret) + tracefs_found = true; + + return ret; +} + +/* mount the tracefs somewhere if it's not mounted */ +char *tracefs_mount(const char *mountpoint) +{ + /* see if it's already mounted */ + if (tracefs_find_mountpoint()) + goto out; + + /* if not mounted and no argument */ + if (mountpoint == NULL) { + /* see if environment variable set */ + mountpoint = getenv(PERF_TRACEFS_ENVIRONMENT); + /* if no environment variable, use default */ + if (mountpoint == NULL) + mountpoint = TRACEFS_DEFAULT_PATH; + } + + if (mount(NULL, mountpoint, "tracefs", 0, NULL) < 0) + return NULL; + + /* save the mountpoint */ + tracefs_found = true; + strncpy(tracefs_mountpoint, mountpoint, sizeof(tracefs_mountpoint)); +out: + return tracefs_mountpoint; +} diff --git a/kernel/tools/lib/api/fs/tracefs.h b/kernel/tools/lib/api/fs/tracefs.h new file mode 100644 index 000000000..da780ac49 --- /dev/null +++ b/kernel/tools/lib/api/fs/tracefs.h @@ -0,0 +1,21 @@ +#ifndef __API_TRACEFS_H__ +#define __API_TRACEFS_H__ + +#include "findfs.h" + +#ifndef TRACEFS_MAGIC +#define TRACEFS_MAGIC 0x74726163 +#endif + +#ifndef PERF_TRACEFS_ENVIRONMENT +#define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR" +#endif + +bool tracefs_configured(void); +const char *tracefs_find_mountpoint(void); +int tracefs_valid_mountpoint(const char *debugfs); +char *tracefs_mount(const char *mountpoint); + +extern char tracefs_mountpoint[]; + +#endif /* __API_DEBUGFS_H__ */ diff --git a/kernel/tools/lib/lockdep/.gitignore b/kernel/tools/lib/lockdep/.gitignore new file mode 100644 index 000000000..cc0e7a9f9 --- /dev/null +++ b/kernel/tools/lib/lockdep/.gitignore @@ -0,0 +1 @@ +liblockdep.so.* diff --git a/kernel/tools/lib/lockdep/Build b/kernel/tools/lib/lockdep/Build new file mode 100644 index 000000000..6f667355b --- /dev/null +++ b/kernel/tools/lib/lockdep/Build @@ -0,0 +1 @@ +liblockdep-y += common.o lockdep.o preload.o rbtree.o diff --git a/kernel/tools/lib/lockdep/Makefile b/kernel/tools/lib/lockdep/Makefile new file mode 100644 index 000000000..18ffccf00 --- /dev/null +++ b/kernel/tools/lib/lockdep/Makefile @@ -0,0 +1,158 @@ +# file format version +FILE_VERSION = 1 + +LIBLOCKDEP_VERSION=$(shell make --no-print-directory -sC ../../.. kernelversion) + +# Makefiles suck: This macro sets a default value of $(2) for the +# variable named by $(1), unless the variable has been set by +# environment or command line. This is necessary for CC and AR +# because make sets default values, so the simpler ?= approach +# won't work as expected. +define allow-override + $(if $(or $(findstring environment,$(origin $(1))),\ + $(findstring command line,$(origin $(1)))),,\ + $(eval $(1) = $(2))) +endef + +# Allow setting CC and AR and LD, or setting CROSS_COMPILE as a prefix. +$(call allow-override,CC,$(CROSS_COMPILE)gcc) +$(call allow-override,AR,$(CROSS_COMPILE)ar) +$(call allow-override,LD,$(CROSS_COMPILE)ld) + +INSTALL = install + +# Use DESTDIR for installing into a different root directory. +# This is useful for building a package. The program will be +# installed in this directory as if it was the root directory. +# Then the build tool can move it later. +DESTDIR ?= +DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' + +prefix ?= /usr/local +libdir_relative = lib +libdir = $(prefix)/$(libdir_relative) +bindir_relative = bin +bindir = $(prefix)/$(bindir_relative) + +export DESTDIR DESTDIR_SQ INSTALL + +MAKEFLAGS += --no-print-directory + +include ../../scripts/Makefile.include + +# copy a bit from Linux kbuild + +ifeq ("$(origin V)", "command line") + VERBOSE = $(V) +endif +ifndef VERBOSE + VERBOSE = 0 +endif + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(shell pwd))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +#$(info Determined 'srctree' to be $(srctree)) +endif + +# Shell quotes +libdir_SQ = $(subst ','\'',$(libdir)) +bindir_SQ = $(subst ','\'',$(bindir)) + +LIB_IN := $(OUTPUT)liblockdep-in.o + +BIN_FILE = lockdep +LIB_FILE = $(OUTPUT)liblockdep.a $(OUTPUT)liblockdep.so.$(LIBLOCKDEP_VERSION) + +CONFIG_INCLUDES = +CONFIG_LIBS = +CONFIG_FLAGS = + +OBJ = $@ +N = + +export Q VERBOSE + +INCLUDES = -I. -I./uinclude -I./include -I../../include $(CONFIG_INCLUDES) + +# Set compile option CFLAGS if not set elsewhere +CFLAGS ?= -g -DCONFIG_LOCKDEP -DCONFIG_STACKTRACE -DCONFIG_PROVE_LOCKING -DBITS_PER_LONG=__WORDSIZE -DLIBLOCKDEP_VERSION='"$(LIBLOCKDEP_VERSION)"' -rdynamic -O0 -g +CFLAGS += -fPIC + +override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ) + +ifeq ($(VERBOSE),1) + Q = + print_shared_lib_compile = + print_install = +else + Q = @ + print_shared_lib_compile = echo ' LD '$(OBJ); + print_static_lib_build = echo ' LD '$(OBJ); + print_install = echo ' INSTALL '$1' to $(DESTDIR_SQ)$2'; +endif + +export srctree OUTPUT CC LD CFLAGS V +build := -f $(srctree)/tools/build/Makefile.build dir=. obj + +do_compile_shared_library = \ + ($(print_shared_lib_compile) \ + $(CC) --shared $^ -o $@ -lpthread -ldl -Wl,-soname='"$@"';$(shell ln -s $@ liblockdep.so)) + +do_build_static_lib = \ + ($(print_static_lib_build) \ + $(RM) $@; $(AR) rcs $@ $^) + +CMD_TARGETS = $(LIB_FILE) + +TARGETS = $(CMD_TARGETS) + + +all: all_cmd + +all_cmd: $(CMD_TARGETS) + +$(LIB_IN): force + $(Q)$(MAKE) $(build)=liblockdep + +liblockdep.so.$(LIBLOCKDEP_VERSION): $(LIB_IN) + $(Q)$(do_compile_shared_library) + +liblockdep.a: $(LIB_IN) + $(Q)$(do_build_static_lib) + +tags: force + $(RM) tags + find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px \ + --regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/' + +TAGS: force + $(RM) TAGS + find . -name '*.[ch]' | xargs etags \ + --regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/' + +define do_install + $(print_install) \ + if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ + fi; \ + $(INSTALL) $1 '$(DESTDIR_SQ)$2' +endef + +install_lib: all_cmd + $(Q)$(call do_install,$(LIB_FILE),$(libdir_SQ)) + $(Q)$(call do_install,$(BIN_FILE),$(bindir_SQ)) + +install: install_lib + +clean: + $(RM) *.o *~ $(TARGETS) *.a *liblockdep*.so* $(VERSION_FILES) .*.d + $(RM) tags TAGS + +PHONY += force +force: + +# Declare the contents of the .PHONY variable as phony. We keep that +# information in a variable so we can use it in if_changed and friends. +.PHONY: $(PHONY) diff --git a/kernel/tools/lib/lockdep/common.c b/kernel/tools/lib/lockdep/common.c new file mode 100644 index 000000000..8ef602f18 --- /dev/null +++ b/kernel/tools/lib/lockdep/common.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include +#include + +static __thread struct task_struct current_obj; + +/* lockdep wants these */ +bool debug_locks = true; +bool debug_locks_silent; + +__attribute__((constructor)) static void liblockdep_init(void) +{ + lockdep_init(); +} + +__attribute__((destructor)) static void liblockdep_exit(void) +{ + debug_check_no_locks_held(¤t_obj); +} + +struct task_struct *__curr(void) +{ + if (current_obj.pid == 0) { + /* Makes lockdep output pretty */ + prctl(PR_GET_NAME, current_obj.comm); + current_obj.pid = syscall(__NR_gettid); + } + + return ¤t_obj; +} diff --git a/kernel/tools/lib/lockdep/include/liblockdep/common.h b/kernel/tools/lib/lockdep/include/liblockdep/common.h new file mode 100644 index 000000000..0bda63002 --- /dev/null +++ b/kernel/tools/lib/lockdep/include/liblockdep/common.h @@ -0,0 +1,50 @@ +#ifndef _LIBLOCKDEP_COMMON_H +#define _LIBLOCKDEP_COMMON_H + +#include + +#define NR_LOCKDEP_CACHING_CLASSES 2 +#define MAX_LOCKDEP_SUBCLASSES 8UL + +#ifndef CALLER_ADDR0 +#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#endif + +#ifndef _RET_IP_ +#define _RET_IP_ CALLER_ADDR0 +#endif + +#ifndef _THIS_IP_ +#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) +#endif + +struct lockdep_subclass_key { + char __one_byte; +}; + +struct lock_class_key { + struct lockdep_subclass_key subkeys[MAX_LOCKDEP_SUBCLASSES]; +}; + +struct lockdep_map { + struct lock_class_key *key; + struct lock_class *class_cache[NR_LOCKDEP_CACHING_CLASSES]; + const char *name; +#ifdef CONFIG_LOCK_STAT + int cpu; + unsigned long ip; +#endif +}; + +void lockdep_init_map(struct lockdep_map *lock, const char *name, + struct lock_class_key *key, int subclass); +void lock_acquire(struct lockdep_map *lock, unsigned int subclass, + int trylock, int read, int check, + struct lockdep_map *nest_lock, unsigned long ip); +void lock_release(struct lockdep_map *lock, int nested, + unsigned long ip); + +#define STATIC_LOCKDEP_MAP_INIT(_name, _key) \ + { .name = (_name), .key = (void *)(_key), } + +#endif diff --git a/kernel/tools/lib/lockdep/include/liblockdep/mutex.h b/kernel/tools/lib/lockdep/include/liblockdep/mutex.h new file mode 100644 index 000000000..ee53a4281 --- /dev/null +++ b/kernel/tools/lib/lockdep/include/liblockdep/mutex.h @@ -0,0 +1,70 @@ +#ifndef _LIBLOCKDEP_MUTEX_H +#define _LIBLOCKDEP_MUTEX_H + +#include +#include "common.h" + +struct liblockdep_pthread_mutex { + pthread_mutex_t mutex; + struct lockdep_map dep_map; +}; + +typedef struct liblockdep_pthread_mutex liblockdep_pthread_mutex_t; + +#define LIBLOCKDEP_PTHREAD_MUTEX_INITIALIZER(mtx) \ + (const struct liblockdep_pthread_mutex) { \ + .mutex = PTHREAD_MUTEX_INITIALIZER, \ + .dep_map = STATIC_LOCKDEP_MAP_INIT(#mtx, &((&(mtx))->dep_map)), \ +} + +static inline int __mutex_init(liblockdep_pthread_mutex_t *lock, + const char *name, + struct lock_class_key *key, + const pthread_mutexattr_t *__mutexattr) +{ + lockdep_init_map(&lock->dep_map, name, key, 0); + return pthread_mutex_init(&lock->mutex, __mutexattr); +} + +#define liblockdep_pthread_mutex_init(mutex, mutexattr) \ +({ \ + static struct lock_class_key __key; \ + \ + __mutex_init((mutex), #mutex, &__key, (mutexattr)); \ +}) + +static inline int liblockdep_pthread_mutex_lock(liblockdep_pthread_mutex_t *lock) +{ + lock_acquire(&lock->dep_map, 0, 0, 0, 1, NULL, (unsigned long)_RET_IP_); + return pthread_mutex_lock(&lock->mutex); +} + +static inline int liblockdep_pthread_mutex_unlock(liblockdep_pthread_mutex_t *lock) +{ + lock_release(&lock->dep_map, 0, (unsigned long)_RET_IP_); + return pthread_mutex_unlock(&lock->mutex); +} + +static inline int liblockdep_pthread_mutex_trylock(liblockdep_pthread_mutex_t *lock) +{ + lock_acquire(&lock->dep_map, 0, 1, 0, 1, NULL, (unsigned long)_RET_IP_); + return pthread_mutex_trylock(&lock->mutex) == 0 ? 1 : 0; +} + +static inline int liblockdep_pthread_mutex_destroy(liblockdep_pthread_mutex_t *lock) +{ + return pthread_mutex_destroy(&lock->mutex); +} + +#ifdef __USE_LIBLOCKDEP + +#define pthread_mutex_t liblockdep_pthread_mutex_t +#define pthread_mutex_init liblockdep_pthread_mutex_init +#define pthread_mutex_lock liblockdep_pthread_mutex_lock +#define pthread_mutex_unlock liblockdep_pthread_mutex_unlock +#define pthread_mutex_trylock liblockdep_pthread_mutex_trylock +#define pthread_mutex_destroy liblockdep_pthread_mutex_destroy + +#endif + +#endif diff --git a/kernel/tools/lib/lockdep/include/liblockdep/rwlock.h b/kernel/tools/lib/lockdep/include/liblockdep/rwlock.h new file mode 100644 index 000000000..4ec03f861 --- /dev/null +++ b/kernel/tools/lib/lockdep/include/liblockdep/rwlock.h @@ -0,0 +1,86 @@ +#ifndef _LIBLOCKDEP_RWLOCK_H +#define _LIBLOCKDEP_RWLOCK_H + +#include +#include "common.h" + +struct liblockdep_pthread_rwlock { + pthread_rwlock_t rwlock; + struct lockdep_map dep_map; +}; + +typedef struct liblockdep_pthread_rwlock liblockdep_pthread_rwlock_t; + +#define LIBLOCKDEP_PTHREAD_RWLOCK_INITIALIZER(rwl) \ + (struct liblockdep_pthread_rwlock) { \ + .rwlock = PTHREAD_RWLOCK_INITIALIZER, \ + .dep_map = STATIC_LOCKDEP_MAP_INIT(#rwl, &((&(rwl))->dep_map)), \ +} + +static inline int __rwlock_init(liblockdep_pthread_rwlock_t *lock, + const char *name, + struct lock_class_key *key, + const pthread_rwlockattr_t *attr) +{ + lockdep_init_map(&lock->dep_map, name, key, 0); + + return pthread_rwlock_init(&lock->rwlock, attr); +} + +#define liblockdep_pthread_rwlock_init(lock, attr) \ +({ \ + static struct lock_class_key __key; \ + \ + __rwlock_init((lock), #lock, &__key, (attr)); \ +}) + +static inline int liblockdep_pthread_rwlock_rdlock(liblockdep_pthread_rwlock_t *lock) +{ + lock_acquire(&lock->dep_map, 0, 0, 2, 1, NULL, (unsigned long)_RET_IP_); + return pthread_rwlock_rdlock(&lock->rwlock); + +} + +static inline int liblockdep_pthread_rwlock_unlock(liblockdep_pthread_rwlock_t *lock) +{ + lock_release(&lock->dep_map, 0, (unsigned long)_RET_IP_); + return pthread_rwlock_unlock(&lock->rwlock); +} + +static inline int liblockdep_pthread_rwlock_wrlock(liblockdep_pthread_rwlock_t *lock) +{ + lock_acquire(&lock->dep_map, 0, 0, 0, 1, NULL, (unsigned long)_RET_IP_); + return pthread_rwlock_wrlock(&lock->rwlock); +} + +static inline int liblockdep_pthread_rwlock_tryrdlock(liblockdep_pthread_rwlock_t *lock) +{ + lock_acquire(&lock->dep_map, 0, 1, 2, 1, NULL, (unsigned long)_RET_IP_); + return pthread_rwlock_tryrdlock(&lock->rwlock) == 0 ? 1 : 0; +} + +static inline int liblockdep_pthread_rwlock_trywlock(liblockdep_pthread_rwlock_t *lock) +{ + lock_acquire(&lock->dep_map, 0, 1, 0, 1, NULL, (unsigned long)_RET_IP_); + return pthread_rwlock_trywlock(&lock->rwlock) == 0 ? 1 : 0; +} + +static inline int liblockdep_rwlock_destroy(liblockdep_pthread_rwlock_t *lock) +{ + return pthread_rwlock_destroy(&lock->rwlock); +} + +#ifdef __USE_LIBLOCKDEP + +#define pthread_rwlock_t liblockdep_pthread_rwlock_t +#define pthread_rwlock_init liblockdep_pthread_rwlock_init +#define pthread_rwlock_rdlock liblockdep_pthread_rwlock_rdlock +#define pthread_rwlock_unlock liblockdep_pthread_rwlock_unlock +#define pthread_rwlock_wrlock liblockdep_pthread_rwlock_wrlock +#define pthread_rwlock_tryrdlock liblockdep_pthread_rwlock_tryrdlock +#define pthread_rwlock_trywlock liblockdep_pthread_rwlock_trywlock +#define pthread_rwlock_destroy liblockdep_rwlock_destroy + +#endif + +#endif diff --git a/kernel/tools/lib/lockdep/lockdep b/kernel/tools/lib/lockdep/lockdep new file mode 100755 index 000000000..49af9fe19 --- /dev/null +++ b/kernel/tools/lib/lockdep/lockdep @@ -0,0 +1,3 @@ +#!/bin/bash + +LD_PRELOAD="./liblockdep.so $LD_PRELOAD" "$@" diff --git a/kernel/tools/lib/lockdep/lockdep.c b/kernel/tools/lib/lockdep/lockdep.c new file mode 100644 index 000000000..f42b7e9aa --- /dev/null +++ b/kernel/tools/lib/lockdep/lockdep.c @@ -0,0 +1,2 @@ +#include +#include "../../../kernel/locking/lockdep.c" diff --git a/kernel/tools/lib/lockdep/lockdep_internals.h b/kernel/tools/lib/lockdep/lockdep_internals.h new file mode 100644 index 000000000..29d0c954c --- /dev/null +++ b/kernel/tools/lib/lockdep/lockdep_internals.h @@ -0,0 +1 @@ +#include "../../../kernel/locking/lockdep_internals.h" diff --git a/kernel/tools/lib/lockdep/lockdep_states.h b/kernel/tools/lib/lockdep/lockdep_states.h new file mode 100644 index 000000000..248d235ef --- /dev/null +++ b/kernel/tools/lib/lockdep/lockdep_states.h @@ -0,0 +1 @@ +#include "../../../kernel/locking/lockdep_states.h" diff --git a/kernel/tools/lib/lockdep/preload.c b/kernel/tools/lib/lockdep/preload.c new file mode 100644 index 000000000..0b0112c80 --- /dev/null +++ b/kernel/tools/lib/lockdep/preload.c @@ -0,0 +1,445 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "include/liblockdep/mutex.h" +#include "../../../include/linux/rbtree.h" + +/** + * struct lock_lookup - liblockdep's view of a single unique lock + * @orig: pointer to the original pthread lock, used for lookups + * @dep_map: lockdep's dep_map structure + * @key: lockdep's key structure + * @node: rb-tree node used to store the lock in a global tree + * @name: a unique name for the lock + */ +struct lock_lookup { + void *orig; /* Original pthread lock, used for lookups */ + struct lockdep_map dep_map; /* Since all locks are dynamic, we need + * a dep_map and a key for each lock */ + /* + * Wait, there's no support for key classes? Yup :( + * Most big projects wrap the pthread api with their own calls to + * be compatible with different locking methods. This means that + * "classes" will be brokes since the function that creates all + * locks will point to a generic locking function instead of the + * actual code that wants to do the locking. + */ + struct lock_class_key key; + struct rb_node node; +#define LIBLOCKDEP_MAX_LOCK_NAME 22 + char name[LIBLOCKDEP_MAX_LOCK_NAME]; +}; + +/* This is where we store our locks */ +static struct rb_root locks = RB_ROOT; +static pthread_rwlock_t locks_rwlock = PTHREAD_RWLOCK_INITIALIZER; + +/* pthread mutex API */ + +#ifdef __GLIBC__ +extern int __pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); +extern int __pthread_mutex_lock(pthread_mutex_t *mutex); +extern int __pthread_mutex_trylock(pthread_mutex_t *mutex); +extern int __pthread_mutex_unlock(pthread_mutex_t *mutex); +extern int __pthread_mutex_destroy(pthread_mutex_t *mutex); +#else +#define __pthread_mutex_init NULL +#define __pthread_mutex_lock NULL +#define __pthread_mutex_trylock NULL +#define __pthread_mutex_unlock NULL +#define __pthread_mutex_destroy NULL +#endif +static int (*ll_pthread_mutex_init)(pthread_mutex_t *mutex, + const pthread_mutexattr_t *attr) = __pthread_mutex_init; +static int (*ll_pthread_mutex_lock)(pthread_mutex_t *mutex) = __pthread_mutex_lock; +static int (*ll_pthread_mutex_trylock)(pthread_mutex_t *mutex) = __pthread_mutex_trylock; +static int (*ll_pthread_mutex_unlock)(pthread_mutex_t *mutex) = __pthread_mutex_unlock; +static int (*ll_pthread_mutex_destroy)(pthread_mutex_t *mutex) = __pthread_mutex_destroy; + +/* pthread rwlock API */ + +#ifdef __GLIBC__ +extern int __pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr); +extern int __pthread_rwlock_destroy(pthread_rwlock_t *rwlock); +extern int __pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); +extern int __pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); +extern int __pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); +extern int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); +extern int __pthread_rwlock_unlock(pthread_rwlock_t *rwlock); +#else +#define __pthread_rwlock_init NULL +#define __pthread_rwlock_destroy NULL +#define __pthread_rwlock_wrlock NULL +#define __pthread_rwlock_trywrlock NULL +#define __pthread_rwlock_rdlock NULL +#define __pthread_rwlock_tryrdlock NULL +#define __pthread_rwlock_unlock NULL +#endif + +static int (*ll_pthread_rwlock_init)(pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr) = __pthread_rwlock_init; +static int (*ll_pthread_rwlock_destroy)(pthread_rwlock_t *rwlock) = __pthread_rwlock_destroy; +static int (*ll_pthread_rwlock_rdlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_rdlock; +static int (*ll_pthread_rwlock_tryrdlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_tryrdlock; +static int (*ll_pthread_rwlock_trywrlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_trywrlock; +static int (*ll_pthread_rwlock_wrlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_wrlock; +static int (*ll_pthread_rwlock_unlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_unlock; + +enum { none, prepare, done, } __init_state; +static void init_preload(void); +static void try_init_preload(void) +{ + if (__init_state != done) + init_preload(); +} + +static struct rb_node **__get_lock_node(void *lock, struct rb_node **parent) +{ + struct rb_node **node = &locks.rb_node; + struct lock_lookup *l; + + *parent = NULL; + + while (*node) { + l = rb_entry(*node, struct lock_lookup, node); + + *parent = *node; + if (lock < l->orig) + node = &l->node.rb_left; + else if (lock > l->orig) + node = &l->node.rb_right; + else + return node; + } + + return node; +} + +#ifndef LIBLOCKDEP_STATIC_ENTRIES +#define LIBLOCKDEP_STATIC_ENTRIES 1024 +#endif + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static struct lock_lookup __locks[LIBLOCKDEP_STATIC_ENTRIES]; +static int __locks_nr; + +static inline bool is_static_lock(struct lock_lookup *lock) +{ + return lock >= __locks && lock < __locks + ARRAY_SIZE(__locks); +} + +static struct lock_lookup *alloc_lock(void) +{ + if (__init_state != done) { + /* + * Some programs attempt to initialize and use locks in their + * allocation path. This means that a call to malloc() would + * result in locks being initialized and locked. + * + * Why is it an issue for us? dlsym() below will try allocating + * to give us the original function. Since this allocation will + * result in a locking operations, we have to let pthread deal + * with it, but we can't! we don't have the pointer to the + * original API since we're inside dlsym() trying to get it + */ + + int idx = __locks_nr++; + if (idx >= ARRAY_SIZE(__locks)) { + fprintf(stderr, + "LOCKDEP error: insufficient LIBLOCKDEP_STATIC_ENTRIES\n"); + exit(EX_UNAVAILABLE); + } + return __locks + idx; + } + + return malloc(sizeof(struct lock_lookup)); +} + +static inline void free_lock(struct lock_lookup *lock) +{ + if (likely(!is_static_lock(lock))) + free(lock); +} + +/** + * __get_lock - find or create a lock instance + * @lock: pointer to a pthread lock function + * + * Try to find an existing lock in the rbtree using the provided pointer. If + * one wasn't found - create it. + */ +static struct lock_lookup *__get_lock(void *lock) +{ + struct rb_node **node, *parent; + struct lock_lookup *l; + + ll_pthread_rwlock_rdlock(&locks_rwlock); + node = __get_lock_node(lock, &parent); + ll_pthread_rwlock_unlock(&locks_rwlock); + if (*node) { + return rb_entry(*node, struct lock_lookup, node); + } + + /* We didn't find the lock, let's create it */ + l = alloc_lock(); + if (l == NULL) + return NULL; + + l->orig = lock; + /* + * Currently the name of the lock is the ptr value of the pthread lock, + * while not optimal, it makes debugging a bit easier. + * + * TODO: Get the real name of the lock using libdwarf + */ + sprintf(l->name, "%p", lock); + lockdep_init_map(&l->dep_map, l->name, &l->key, 0); + + ll_pthread_rwlock_wrlock(&locks_rwlock); + /* This might have changed since the last time we fetched it */ + node = __get_lock_node(lock, &parent); + rb_link_node(&l->node, parent, node); + rb_insert_color(&l->node, &locks); + ll_pthread_rwlock_unlock(&locks_rwlock); + + return l; +} + +static void __del_lock(struct lock_lookup *lock) +{ + ll_pthread_rwlock_wrlock(&locks_rwlock); + rb_erase(&lock->node, &locks); + ll_pthread_rwlock_unlock(&locks_rwlock); + free_lock(lock); +} + +int pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *attr) +{ + int r; + + /* + * We keep trying to init our preload module because there might be + * code in init sections that tries to touch locks before we are + * initialized, in that case we'll need to manually call preload + * to get us going. + * + * Funny enough, kernel's lockdep had the same issue, and used + * (almost) the same solution. See look_up_lock_class() in + * kernel/locking/lockdep.c for details. + */ + try_init_preload(); + + r = ll_pthread_mutex_init(mutex, attr); + if (r == 0) + /* + * We do a dummy initialization here so that lockdep could + * warn us if something fishy is going on - such as + * initializing a held lock. + */ + __get_lock(mutex); + + return r; +} + +int pthread_mutex_lock(pthread_mutex_t *mutex) +{ + int r; + + try_init_preload(); + + lock_acquire(&__get_lock(mutex)->dep_map, 0, 0, 0, 1, NULL, + (unsigned long)_RET_IP_); + /* + * Here's the thing with pthread mutexes: unlike the kernel variant, + * they can fail. + * + * This means that the behaviour here is a bit different from what's + * going on in the kernel: there we just tell lockdep that we took the + * lock before actually taking it, but here we must deal with the case + * that locking failed. + * + * To do that we'll "release" the lock if locking failed - this way + * we'll get lockdep doing the correct checks when we try to take + * the lock, and if that fails - we'll be back to the correct + * state by releasing it. + */ + r = ll_pthread_mutex_lock(mutex); + if (r) + lock_release(&__get_lock(mutex)->dep_map, 0, (unsigned long)_RET_IP_); + + return r; +} + +int pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + int r; + + try_init_preload(); + + lock_acquire(&__get_lock(mutex)->dep_map, 0, 1, 0, 1, NULL, (unsigned long)_RET_IP_); + r = ll_pthread_mutex_trylock(mutex); + if (r) + lock_release(&__get_lock(mutex)->dep_map, 0, (unsigned long)_RET_IP_); + + return r; +} + +int pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + int r; + + try_init_preload(); + + lock_release(&__get_lock(mutex)->dep_map, 0, (unsigned long)_RET_IP_); + /* + * Just like taking a lock, only in reverse! + * + * If we fail releasing the lock, tell lockdep we're holding it again. + */ + r = ll_pthread_mutex_unlock(mutex); + if (r) + lock_acquire(&__get_lock(mutex)->dep_map, 0, 0, 0, 1, NULL, (unsigned long)_RET_IP_); + + return r; +} + +int pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + try_init_preload(); + + /* + * Let's see if we're releasing a lock that's held. + * + * TODO: Hook into free() and add that check there as well. + */ + debug_check_no_locks_freed(mutex, sizeof(*mutex)); + __del_lock(__get_lock(mutex)); + return ll_pthread_mutex_destroy(mutex); +} + +/* This is the rwlock part, very similar to what happened with mutex above */ +int pthread_rwlock_init(pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr) +{ + int r; + + try_init_preload(); + + r = ll_pthread_rwlock_init(rwlock, attr); + if (r == 0) + __get_lock(rwlock); + + return r; +} + +int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) +{ + try_init_preload(); + + debug_check_no_locks_freed(rwlock, sizeof(*rwlock)); + __del_lock(__get_lock(rwlock)); + return ll_pthread_rwlock_destroy(rwlock); +} + +int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) +{ + int r; + + init_preload(); + + lock_acquire(&__get_lock(rwlock)->dep_map, 0, 0, 2, 1, NULL, (unsigned long)_RET_IP_); + r = ll_pthread_rwlock_rdlock(rwlock); + if (r) + lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_); + + return r; +} + +int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) +{ + int r; + + init_preload(); + + lock_acquire(&__get_lock(rwlock)->dep_map, 0, 1, 2, 1, NULL, (unsigned long)_RET_IP_); + r = ll_pthread_rwlock_tryrdlock(rwlock); + if (r) + lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_); + + return r; +} + +int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) +{ + int r; + + init_preload(); + + lock_acquire(&__get_lock(rwlock)->dep_map, 0, 1, 0, 1, NULL, (unsigned long)_RET_IP_); + r = ll_pthread_rwlock_trywrlock(rwlock); + if (r) + lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_); + + return r; +} + +int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) +{ + int r; + + init_preload(); + + lock_acquire(&__get_lock(rwlock)->dep_map, 0, 0, 0, 1, NULL, (unsigned long)_RET_IP_); + r = ll_pthread_rwlock_wrlock(rwlock); + if (r) + lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_); + + return r; +} + +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) +{ + int r; + + init_preload(); + + lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_); + r = ll_pthread_rwlock_unlock(rwlock); + if (r) + lock_acquire(&__get_lock(rwlock)->dep_map, 0, 0, 0, 1, NULL, (unsigned long)_RET_IP_); + + return r; +} + +__attribute__((constructor)) static void init_preload(void) +{ + if (__init_state == done) + return; + +#ifndef __GLIBC__ + __init_state = prepare; + + ll_pthread_mutex_init = dlsym(RTLD_NEXT, "pthread_mutex_init"); + ll_pthread_mutex_lock = dlsym(RTLD_NEXT, "pthread_mutex_lock"); + ll_pthread_mutex_trylock = dlsym(RTLD_NEXT, "pthread_mutex_trylock"); + ll_pthread_mutex_unlock = dlsym(RTLD_NEXT, "pthread_mutex_unlock"); + ll_pthread_mutex_destroy = dlsym(RTLD_NEXT, "pthread_mutex_destroy"); + + ll_pthread_rwlock_init = dlsym(RTLD_NEXT, "pthread_rwlock_init"); + ll_pthread_rwlock_destroy = dlsym(RTLD_NEXT, "pthread_rwlock_destroy"); + ll_pthread_rwlock_rdlock = dlsym(RTLD_NEXT, "pthread_rwlock_rdlock"); + ll_pthread_rwlock_tryrdlock = dlsym(RTLD_NEXT, "pthread_rwlock_tryrdlock"); + ll_pthread_rwlock_wrlock = dlsym(RTLD_NEXT, "pthread_rwlock_wrlock"); + ll_pthread_rwlock_trywrlock = dlsym(RTLD_NEXT, "pthread_rwlock_trywrlock"); + ll_pthread_rwlock_unlock = dlsym(RTLD_NEXT, "pthread_rwlock_unlock"); +#endif + + lockdep_init(); + + __init_state = done; +} diff --git a/kernel/tools/lib/lockdep/rbtree.c b/kernel/tools/lib/lockdep/rbtree.c new file mode 100644 index 000000000..f7f43033c --- /dev/null +++ b/kernel/tools/lib/lockdep/rbtree.c @@ -0,0 +1 @@ +#include "../../../lib/rbtree.c" diff --git a/kernel/tools/lib/lockdep/run_tests.sh b/kernel/tools/lib/lockdep/run_tests.sh new file mode 100755 index 000000000..5334ad9d3 --- /dev/null +++ b/kernel/tools/lib/lockdep/run_tests.sh @@ -0,0 +1,27 @@ +#! /bin/bash + +make &> /dev/null + +for i in `ls tests/*.c`; do + testname=$(basename -s .c "$i") + gcc -o tests/$testname -pthread -lpthread $i liblockdep.a -Iinclude -D__USE_LIBLOCKDEP &> /dev/null + echo -ne "$testname... " + if [ $(timeout 1 ./tests/$testname | wc -l) -gt 0 ]; then + echo "PASSED!" + else + echo "FAILED!" + fi + rm tests/$testname +done + +for i in `ls tests/*.c`; do + testname=$(basename -s .c "$i") + gcc -o tests/$testname -pthread -lpthread -Iinclude $i &> /dev/null + echo -ne "(PRELOAD) $testname... " + if [ $(timeout 1 ./lockdep ./tests/$testname | wc -l) -gt 0 ]; then + echo "PASSED!" + else + echo "FAILED!" + fi + rm tests/$testname +done diff --git a/kernel/tools/lib/lockdep/tests/AA.c b/kernel/tools/lib/lockdep/tests/AA.c new file mode 100644 index 000000000..0f782ff40 --- /dev/null +++ b/kernel/tools/lib/lockdep/tests/AA.c @@ -0,0 +1,13 @@ +#include + +void main(void) +{ + pthread_mutex_t a, b; + + pthread_mutex_init(&a, NULL); + pthread_mutex_init(&b, NULL); + + pthread_mutex_lock(&a); + pthread_mutex_lock(&b); + pthread_mutex_lock(&a); +} diff --git a/kernel/tools/lib/lockdep/tests/ABBA.c b/kernel/tools/lib/lockdep/tests/ABBA.c new file mode 100644 index 000000000..07f0e29d5 --- /dev/null +++ b/kernel/tools/lib/lockdep/tests/ABBA.c @@ -0,0 +1,13 @@ +#include +#include "common.h" + +void main(void) +{ + pthread_mutex_t a, b; + + pthread_mutex_init(&a, NULL); + pthread_mutex_init(&b, NULL); + + LOCK_UNLOCK_2(a, b); + LOCK_UNLOCK_2(b, a); +} diff --git a/kernel/tools/lib/lockdep/tests/ABBCCA.c b/kernel/tools/lib/lockdep/tests/ABBCCA.c new file mode 100644 index 000000000..843db09ac --- /dev/null +++ b/kernel/tools/lib/lockdep/tests/ABBCCA.c @@ -0,0 +1,15 @@ +#include +#include "common.h" + +void main(void) +{ + pthread_mutex_t a, b, c; + + pthread_mutex_init(&a, NULL); + pthread_mutex_init(&b, NULL); + pthread_mutex_init(&c, NULL); + + LOCK_UNLOCK_2(a, b); + LOCK_UNLOCK_2(b, c); + LOCK_UNLOCK_2(c, a); +} diff --git a/kernel/tools/lib/lockdep/tests/ABBCCDDA.c b/kernel/tools/lib/lockdep/tests/ABBCCDDA.c new file mode 100644 index 000000000..33620e268 --- /dev/null +++ b/kernel/tools/lib/lockdep/tests/ABBCCDDA.c @@ -0,0 +1,17 @@ +#include +#include "common.h" + +void main(void) +{ + pthread_mutex_t a, b, c, d; + + pthread_mutex_init(&a, NULL); + pthread_mutex_init(&b, NULL); + pthread_mutex_init(&c, NULL); + pthread_mutex_init(&d, NULL); + + LOCK_UNLOCK_2(a, b); + LOCK_UNLOCK_2(b, c); + LOCK_UNLOCK_2(c, d); + LOCK_UNLOCK_2(d, a); +} diff --git a/kernel/tools/lib/lockdep/tests/ABCABC.c b/kernel/tools/lib/lockdep/tests/ABCABC.c new file mode 100644 index 000000000..3fee51e3a --- /dev/null +++ b/kernel/tools/lib/lockdep/tests/ABCABC.c @@ -0,0 +1,15 @@ +#include +#include "common.h" + +void main(void) +{ + pthread_mutex_t a, b, c; + + pthread_mutex_init(&a, NULL); + pthread_mutex_init(&b, NULL); + pthread_mutex_init(&c, NULL); + + LOCK_UNLOCK_2(a, b); + LOCK_UNLOCK_2(c, a); + LOCK_UNLOCK_2(b, c); +} diff --git a/kernel/tools/lib/lockdep/tests/ABCDBCDA.c b/kernel/tools/lib/lockdep/tests/ABCDBCDA.c new file mode 100644 index 000000000..427ba562c --- /dev/null +++ b/kernel/tools/lib/lockdep/tests/ABCDBCDA.c @@ -0,0 +1,17 @@ +#include +#include "common.h" + +void main(void) +{ + pthread_mutex_t a, b, c, d; + + pthread_mutex_init(&a, NULL); + pthread_mutex_init(&b, NULL); + pthread_mutex_init(&c, NULL); + pthread_mutex_init(&d, NULL); + + LOCK_UNLOCK_2(a, b); + LOCK_UNLOCK_2(c, d); + LOCK_UNLOCK_2(b, c); + LOCK_UNLOCK_2(d, a); +} diff --git a/kernel/tools/lib/lockdep/tests/ABCDBDDA.c b/kernel/tools/lib/lockdep/tests/ABCDBDDA.c new file mode 100644 index 000000000..680c6cf3e --- /dev/null +++ b/kernel/tools/lib/lockdep/tests/ABCDBDDA.c @@ -0,0 +1,17 @@ +#include +#include "common.h" + +void main(void) +{ + pthread_mutex_t a, b, c, d; + + pthread_mutex_init(&a, NULL); + pthread_mutex_init(&b, NULL); + pthread_mutex_init(&c, NULL); + pthread_mutex_init(&d, NULL); + + LOCK_UNLOCK_2(a, b); + LOCK_UNLOCK_2(c, d); + LOCK_UNLOCK_2(b, d); + LOCK_UNLOCK_2(d, a); +} diff --git a/kernel/tools/lib/lockdep/tests/WW.c b/kernel/tools/lib/lockdep/tests/WW.c new file mode 100644 index 000000000..d44f77d71 --- /dev/null +++ b/kernel/tools/lib/lockdep/tests/WW.c @@ -0,0 +1,13 @@ +#include + +void main(void) +{ + pthread_rwlock_t a, b; + + pthread_rwlock_init(&a, NULL); + pthread_rwlock_init(&b, NULL); + + pthread_rwlock_wrlock(&a); + pthread_rwlock_rdlock(&b); + pthread_rwlock_wrlock(&a); +} diff --git a/kernel/tools/lib/lockdep/tests/common.h b/kernel/tools/lib/lockdep/tests/common.h new file mode 100644 index 000000000..d89e94d47 --- /dev/null +++ b/kernel/tools/lib/lockdep/tests/common.h @@ -0,0 +1,12 @@ +#ifndef _LIBLOCKDEP_TEST_COMMON_H +#define _LIBLOCKDEP_TEST_COMMON_H + +#define LOCK_UNLOCK_2(a, b) \ + do { \ + pthread_mutex_lock(&(a)); \ + pthread_mutex_lock(&(b)); \ + pthread_mutex_unlock(&(b)); \ + pthread_mutex_unlock(&(a)); \ + } while(0) + +#endif diff --git a/kernel/tools/lib/lockdep/tests/unlock_balance.c b/kernel/tools/lib/lockdep/tests/unlock_balance.c new file mode 100644 index 000000000..0bc62de68 --- /dev/null +++ b/kernel/tools/lib/lockdep/tests/unlock_balance.c @@ -0,0 +1,12 @@ +#include + +void main(void) +{ + pthread_mutex_t a; + + pthread_mutex_init(&a, NULL); + + pthread_mutex_lock(&a); + pthread_mutex_unlock(&a); + pthread_mutex_unlock(&a); +} diff --git a/kernel/tools/lib/lockdep/uinclude/asm/hash.h b/kernel/tools/lib/lockdep/uinclude/asm/hash.h new file mode 100644 index 000000000..d82b170bb --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/asm/hash.h @@ -0,0 +1,6 @@ +#ifndef __ASM_GENERIC_HASH_H +#define __ASM_GENERIC_HASH_H + +/* Stub */ + +#endif /* __ASM_GENERIC_HASH_H */ diff --git a/kernel/tools/lib/lockdep/uinclude/asm/hweight.h b/kernel/tools/lib/lockdep/uinclude/asm/hweight.h new file mode 100644 index 000000000..fab00ff93 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/asm/hweight.h @@ -0,0 +1,3 @@ + +/* empty file */ + diff --git a/kernel/tools/lib/lockdep/uinclude/asm/sections.h b/kernel/tools/lib/lockdep/uinclude/asm/sections.h new file mode 100644 index 000000000..fab00ff93 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/asm/sections.h @@ -0,0 +1,3 @@ + +/* empty file */ + diff --git a/kernel/tools/lib/lockdep/uinclude/linux/bitops.h b/kernel/tools/lib/lockdep/uinclude/linux/bitops.h new file mode 100644 index 000000000..fab00ff93 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/bitops.h @@ -0,0 +1,3 @@ + +/* empty file */ + diff --git a/kernel/tools/lib/lockdep/uinclude/linux/compiler.h b/kernel/tools/lib/lockdep/uinclude/linux/compiler.h new file mode 100644 index 000000000..7ac838a1f --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/compiler.h @@ -0,0 +1,7 @@ +#ifndef _LIBLOCKDEP_LINUX_COMPILER_H_ +#define _LIBLOCKDEP_LINUX_COMPILER_H_ + +#define __used __attribute__((__unused__)) +#define unlikely + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/debug_locks.h b/kernel/tools/lib/lockdep/uinclude/linux/debug_locks.h new file mode 100644 index 000000000..f38eb64df --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/debug_locks.h @@ -0,0 +1,12 @@ +#ifndef _LIBLOCKDEP_DEBUG_LOCKS_H_ +#define _LIBLOCKDEP_DEBUG_LOCKS_H_ + +#include +#include + +#define DEBUG_LOCKS_WARN_ON(x) (x) + +extern bool debug_locks; +extern bool debug_locks_silent; + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/delay.h b/kernel/tools/lib/lockdep/uinclude/linux/delay.h new file mode 100644 index 000000000..fab00ff93 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/delay.h @@ -0,0 +1,3 @@ + +/* empty file */ + diff --git a/kernel/tools/lib/lockdep/uinclude/linux/ftrace.h b/kernel/tools/lib/lockdep/uinclude/linux/ftrace.h new file mode 100644 index 000000000..fab00ff93 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/ftrace.h @@ -0,0 +1,3 @@ + +/* empty file */ + diff --git a/kernel/tools/lib/lockdep/uinclude/linux/gfp.h b/kernel/tools/lib/lockdep/uinclude/linux/gfp.h new file mode 100644 index 000000000..fab00ff93 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/gfp.h @@ -0,0 +1,3 @@ + +/* empty file */ + diff --git a/kernel/tools/lib/lockdep/uinclude/linux/hardirq.h b/kernel/tools/lib/lockdep/uinclude/linux/hardirq.h new file mode 100644 index 000000000..c8f3f8f58 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/hardirq.h @@ -0,0 +1,11 @@ +#ifndef _LIBLOCKDEP_LINUX_HARDIRQ_H_ +#define _LIBLOCKDEP_LINUX_HARDIRQ_H_ + +#define SOFTIRQ_BITS 0UL +#define HARDIRQ_BITS 0UL +#define SOFTIRQ_SHIFT 0UL +#define HARDIRQ_SHIFT 0UL +#define hardirq_count() 0UL +#define softirq_count() 0UL + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/hash.h b/kernel/tools/lib/lockdep/uinclude/linux/hash.h new file mode 100644 index 000000000..0f8479858 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/hash.h @@ -0,0 +1 @@ +#include "../../../include/linux/hash.h" diff --git a/kernel/tools/lib/lockdep/uinclude/linux/interrupt.h b/kernel/tools/lib/lockdep/uinclude/linux/interrupt.h new file mode 100644 index 000000000..fab00ff93 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/interrupt.h @@ -0,0 +1,3 @@ + +/* empty file */ + diff --git a/kernel/tools/lib/lockdep/uinclude/linux/irqflags.h b/kernel/tools/lib/lockdep/uinclude/linux/irqflags.h new file mode 100644 index 000000000..6cc296f0f --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/irqflags.h @@ -0,0 +1,38 @@ +#ifndef _LIBLOCKDEP_LINUX_TRACE_IRQFLAGS_H_ +#define _LIBLOCKDEP_LINUX_TRACE_IRQFLAGS_H_ + +# define trace_hardirq_context(p) 0 +# define trace_softirq_context(p) 0 +# define trace_hardirqs_enabled(p) 0 +# define trace_softirqs_enabled(p) 0 +# define trace_hardirq_enter() do { } while (0) +# define trace_hardirq_exit() do { } while (0) +# define lockdep_softirq_enter() do { } while (0) +# define lockdep_softirq_exit() do { } while (0) +# define INIT_TRACE_IRQFLAGS + +# define stop_critical_timings() do { } while (0) +# define start_critical_timings() do { } while (0) + +#define raw_local_irq_disable() do { } while (0) +#define raw_local_irq_enable() do { } while (0) +#define raw_local_irq_save(flags) ((flags) = 0) +#define raw_local_irq_restore(flags) do { } while (0) +#define raw_local_save_flags(flags) ((flags) = 0) +#define raw_irqs_disabled_flags(flags) do { } while (0) +#define raw_irqs_disabled() 0 +#define raw_safe_halt() + +#define local_irq_enable() do { } while (0) +#define local_irq_disable() do { } while (0) +#define local_irq_save(flags) ((flags) = 0) +#define local_irq_restore(flags) do { } while (0) +#define local_save_flags(flags) ((flags) = 0) +#define irqs_disabled() (1) +#define irqs_disabled_flags(flags) (0) +#define safe_halt() do { } while (0) + +#define trace_lock_release(x, y) +#define trace_lock_acquire(a, b, c, d, e, f, g) + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/kallsyms.h b/kernel/tools/lib/lockdep/uinclude/linux/kallsyms.h new file mode 100644 index 000000000..b0f2dbdf1 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/kallsyms.h @@ -0,0 +1,32 @@ +#ifndef _LIBLOCKDEP_LINUX_KALLSYMS_H_ +#define _LIBLOCKDEP_LINUX_KALLSYMS_H_ + +#include +#include + +#define KSYM_NAME_LEN 128 + +struct module; + +static inline const char *kallsyms_lookup(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset, + char **modname, char *namebuf) +{ + return NULL; +} + +#include +#include +static inline void print_ip_sym(unsigned long ip) +{ + char **name; + + name = backtrace_symbols((void **)&ip, 1); + + printf("%s\n", *name); + + free(name); +} + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/kern_levels.h b/kernel/tools/lib/lockdep/uinclude/linux/kern_levels.h new file mode 100644 index 000000000..3b9bade28 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/kern_levels.h @@ -0,0 +1,25 @@ +#ifndef __KERN_LEVELS_H__ +#define __KERN_LEVELS_H__ + +#define KERN_SOH "" /* ASCII Start Of Header */ +#define KERN_SOH_ASCII '' + +#define KERN_EMERG KERN_SOH "" /* system is unusable */ +#define KERN_ALERT KERN_SOH "" /* action must be taken immediately */ +#define KERN_CRIT KERN_SOH "" /* critical conditions */ +#define KERN_ERR KERN_SOH "" /* error conditions */ +#define KERN_WARNING KERN_SOH "" /* warning conditions */ +#define KERN_NOTICE KERN_SOH "" /* normal but significant condition */ +#define KERN_INFO KERN_SOH "" /* informational */ +#define KERN_DEBUG KERN_SOH "" /* debug-level messages */ + +#define KERN_DEFAULT KERN_SOH "" /* the default kernel loglevel */ + +/* + * Annotation for a "continued" line of log printout (only done after a + * line that had no enclosing \n). Only to be used by core/arch code + * during early bootup (a continued line is not SMP-safe otherwise). + */ +#define KERN_CONT "" + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/kernel.h b/kernel/tools/lib/lockdep/uinclude/linux/kernel.h new file mode 100644 index 000000000..cd2cc59a5 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/kernel.h @@ -0,0 +1,47 @@ +#ifndef _LIBLOCKDEP_LINUX_KERNEL_H_ +#define _LIBLOCKDEP_LINUX_KERNEL_H_ + +#include +#include +#include +#include +#include + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof(((type *)0)->member) * __mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); }) +#endif + +#define max(x, y) ({ \ + typeof(x) _max1 = (x); \ + typeof(y) _max2 = (y); \ + (void) (&_max1 == &_max2); \ + _max1 > _max2 ? _max1 : _max2; }) + +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) +#define WARN_ON(x) (x) +#define WARN_ON_ONCE(x) (x) +#define likely(x) (x) +#define WARN(x, y, z) (x) +#define uninitialized_var(x) x +#define __init +#define noinline +#define list_add_tail_rcu list_add_tail +#define list_for_each_entry_rcu list_for_each_entry +#define barrier() +#define synchronize_sched() + +#ifndef CALLER_ADDR0 +#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#endif + +#ifndef _RET_IP_ +#define _RET_IP_ CALLER_ADDR0 +#endif + +#ifndef _THIS_IP_ +#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) +#endif + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/kmemcheck.h b/kernel/tools/lib/lockdep/uinclude/linux/kmemcheck.h new file mode 100644 index 000000000..94d598bc6 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/kmemcheck.h @@ -0,0 +1,8 @@ +#ifndef _LIBLOCKDEP_LINUX_KMEMCHECK_H_ +#define _LIBLOCKDEP_LINUX_KMEMCHECK_H_ + +static inline void kmemcheck_mark_initialized(void *address, unsigned int n) +{ +} + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/linkage.h b/kernel/tools/lib/lockdep/uinclude/linux/linkage.h new file mode 100644 index 000000000..fab00ff93 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/linkage.h @@ -0,0 +1,3 @@ + +/* empty file */ + diff --git a/kernel/tools/lib/lockdep/uinclude/linux/list.h b/kernel/tools/lib/lockdep/uinclude/linux/list.h new file mode 100644 index 000000000..6e9ef31ed --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/list.h @@ -0,0 +1 @@ +#include "../../../include/linux/list.h" diff --git a/kernel/tools/lib/lockdep/uinclude/linux/lockdep.h b/kernel/tools/lib/lockdep/uinclude/linux/lockdep.h new file mode 100644 index 000000000..c1552c285 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/lockdep.h @@ -0,0 +1,58 @@ +#ifndef _LIBLOCKDEP_LOCKDEP_H_ +#define _LIBLOCKDEP_LOCKDEP_H_ + +#include +#include +#include +#include +#include + + +#define MAX_LOCK_DEPTH 2000UL + +#define asmlinkage +#define __visible + +#include "../../../include/linux/lockdep.h" + +struct task_struct { + u64 curr_chain_key; + int lockdep_depth; + unsigned int lockdep_recursion; + struct held_lock held_locks[MAX_LOCK_DEPTH]; + gfp_t lockdep_reclaim_gfp; + int pid; + char comm[17]; +}; + +extern struct task_struct *__curr(void); + +#define current (__curr()) + +#define debug_locks_off() 1 +#define task_pid_nr(tsk) ((tsk)->pid) + +#define KSYM_NAME_LEN 128 +#define printk printf + +#define list_del_rcu list_del + +#define atomic_t unsigned long +#define atomic_inc(x) ((*(x))++) + +static struct new_utsname *init_utsname(void) +{ + static struct new_utsname n = (struct new_utsname) { + .release = "liblockdep", + .version = LIBLOCKDEP_VERSION, + }; + + return &n; +} + +#define print_tainted() "" +#define static_obj(x) 1 + +#define debug_show_all_locks() + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/module.h b/kernel/tools/lib/lockdep/uinclude/linux/module.h new file mode 100644 index 000000000..09c7a7be8 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/module.h @@ -0,0 +1,6 @@ +#ifndef _LIBLOCKDEP_LINUX_MODULE_H_ +#define _LIBLOCKDEP_LINUX_MODULE_H_ + +#define module_param(name, type, perm) + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/mutex.h b/kernel/tools/lib/lockdep/uinclude/linux/mutex.h new file mode 100644 index 000000000..fab00ff93 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/mutex.h @@ -0,0 +1,3 @@ + +/* empty file */ + diff --git a/kernel/tools/lib/lockdep/uinclude/linux/poison.h b/kernel/tools/lib/lockdep/uinclude/linux/poison.h new file mode 100644 index 000000000..0c27bdf14 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/poison.h @@ -0,0 +1 @@ +#include "../../../include/linux/poison.h" diff --git a/kernel/tools/lib/lockdep/uinclude/linux/prefetch.h b/kernel/tools/lib/lockdep/uinclude/linux/prefetch.h new file mode 100644 index 000000000..d73fe6f85 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/prefetch.h @@ -0,0 +1,6 @@ +#ifndef _LIBLOCKDEP_LINUX_PREFETCH_H_ +#define _LIBLOCKDEP_LINUX_PREFETCH_H + +static inline void prefetch(void *a __attribute__((unused))) { } + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/proc_fs.h b/kernel/tools/lib/lockdep/uinclude/linux/proc_fs.h new file mode 100644 index 000000000..fab00ff93 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/proc_fs.h @@ -0,0 +1,3 @@ + +/* empty file */ + diff --git a/kernel/tools/lib/lockdep/uinclude/linux/rbtree.h b/kernel/tools/lib/lockdep/uinclude/linux/rbtree.h new file mode 100644 index 000000000..965901db4 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/rbtree.h @@ -0,0 +1 @@ +#include "../../../include/linux/rbtree.h" diff --git a/kernel/tools/lib/lockdep/uinclude/linux/rbtree_augmented.h b/kernel/tools/lib/lockdep/uinclude/linux/rbtree_augmented.h new file mode 100644 index 000000000..c37594773 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/rbtree_augmented.h @@ -0,0 +1,2 @@ +#define __always_inline +#include "../../../include/linux/rbtree_augmented.h" diff --git a/kernel/tools/lib/lockdep/uinclude/linux/rcu.h b/kernel/tools/lib/lockdep/uinclude/linux/rcu.h new file mode 100644 index 000000000..042ee8e46 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/rcu.h @@ -0,0 +1,21 @@ +#ifndef _LIBLOCKDEP_RCU_H_ +#define _LIBLOCKDEP_RCU_H_ + +int rcu_scheduler_active; + +static inline int rcu_lockdep_current_cpu_online(void) +{ + return 1; +} + +static inline int rcu_is_cpu_idle(void) +{ + return 1; +} + +static inline bool rcu_is_watching(void) +{ + return false; +} + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/seq_file.h b/kernel/tools/lib/lockdep/uinclude/linux/seq_file.h new file mode 100644 index 000000000..fab00ff93 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/seq_file.h @@ -0,0 +1,3 @@ + +/* empty file */ + diff --git a/kernel/tools/lib/lockdep/uinclude/linux/spinlock.h b/kernel/tools/lib/lockdep/uinclude/linux/spinlock.h new file mode 100644 index 000000000..68c1aa2bc --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/spinlock.h @@ -0,0 +1,25 @@ +#ifndef _LIBLOCKDEP_SPINLOCK_H_ +#define _LIBLOCKDEP_SPINLOCK_H_ + +#include +#include + +#define arch_spinlock_t pthread_mutex_t +#define __ARCH_SPIN_LOCK_UNLOCKED PTHREAD_MUTEX_INITIALIZER + +static inline void arch_spin_lock(arch_spinlock_t *mutex) +{ + pthread_mutex_lock(mutex); +} + +static inline void arch_spin_unlock(arch_spinlock_t *mutex) +{ + pthread_mutex_unlock(mutex); +} + +static inline bool arch_spin_is_locked(arch_spinlock_t *mutex) +{ + return true; +} + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/stacktrace.h b/kernel/tools/lib/lockdep/uinclude/linux/stacktrace.h new file mode 100644 index 000000000..39aecc6b1 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/stacktrace.h @@ -0,0 +1,32 @@ +#ifndef _LIBLOCKDEP_LINUX_STACKTRACE_H_ +#define _LIBLOCKDEP_LINUX_STACKTRACE_H_ + +#include + +struct stack_trace { + unsigned int nr_entries, max_entries; + unsigned long *entries; + int skip; +}; + +static inline void print_stack_trace(struct stack_trace *trace, int spaces) +{ + backtrace_symbols_fd((void **)trace->entries, trace->nr_entries, 1); +} + +#define save_stack_trace(trace) \ + ((trace)->nr_entries = \ + backtrace((void **)(trace)->entries, (trace)->max_entries)) + +static inline int dump_stack(void) +{ + void *array[64]; + size_t size; + + size = backtrace(array, 64); + backtrace_symbols_fd(array, size, 1); + + return 0; +} + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/linux/stringify.h b/kernel/tools/lib/lockdep/uinclude/linux/stringify.h new file mode 100644 index 000000000..05dfcd1ac --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/linux/stringify.h @@ -0,0 +1,7 @@ +#ifndef _LIBLOCKDEP_LINUX_STRINGIFY_H_ +#define _LIBLOCKDEP_LINUX_STRINGIFY_H_ + +#define __stringify_1(x...) #x +#define __stringify(x...) __stringify_1(x) + +#endif diff --git a/kernel/tools/lib/lockdep/uinclude/trace/events/lock.h b/kernel/tools/lib/lockdep/uinclude/trace/events/lock.h new file mode 100644 index 000000000..fab00ff93 --- /dev/null +++ b/kernel/tools/lib/lockdep/uinclude/trace/events/lock.h @@ -0,0 +1,3 @@ + +/* empty file */ + diff --git a/kernel/tools/lib/symbol/kallsyms.c b/kernel/tools/lib/symbol/kallsyms.c new file mode 100644 index 000000000..18bc271a4 --- /dev/null +++ b/kernel/tools/lib/symbol/kallsyms.c @@ -0,0 +1,58 @@ +#include "symbol/kallsyms.h" +#include +#include + +int kallsyms__parse(const char *filename, void *arg, + int (*process_symbol)(void *arg, const char *name, + char type, u64 start)) +{ + char *line = NULL; + size_t n; + int err = -1; + FILE *file = fopen(filename, "r"); + + if (file == NULL) + goto out_failure; + + err = 0; + + while (!feof(file)) { + u64 start; + int line_len, len; + char symbol_type; + char *symbol_name; + + line_len = getline(&line, &n, file); + if (line_len < 0 || !line) + break; + + line[--line_len] = '\0'; /* \n */ + + len = hex2u64(line, &start); + + len++; + if (len + 2 >= line_len) + continue; + + symbol_type = line[len]; + len += 2; + symbol_name = line + len; + len = line_len - len; + + if (len >= KSYM_NAME_LEN) { + err = -1; + break; + } + + err = process_symbol(arg, symbol_name, symbol_type, start); + if (err) + break; + } + + free(line); + fclose(file); + return err; + +out_failure: + return -1; +} diff --git a/kernel/tools/lib/symbol/kallsyms.h b/kernel/tools/lib/symbol/kallsyms.h new file mode 100644 index 000000000..6084f5e18 --- /dev/null +++ b/kernel/tools/lib/symbol/kallsyms.h @@ -0,0 +1,24 @@ +#ifndef __TOOLS_KALLSYMS_H_ +#define __TOOLS_KALLSYMS_H_ 1 + +#include +#include +#include + +#ifndef KSYM_NAME_LEN +#define KSYM_NAME_LEN 256 +#endif + +static inline u8 kallsyms2elf_type(char type) +{ + if (type == 'W') + return STB_WEAK; + + return isupper(type) ? STB_GLOBAL : STB_LOCAL; +} + +int kallsyms__parse(const char *filename, void *arg, + int (*process_symbol)(void *arg, const char *name, + char type, u64 start)); + +#endif /* __TOOLS_KALLSYMS_H_ */ diff --git a/kernel/tools/lib/traceevent/.gitignore b/kernel/tools/lib/traceevent/.gitignore new file mode 100644 index 000000000..35f56be5a --- /dev/null +++ b/kernel/tools/lib/traceevent/.gitignore @@ -0,0 +1 @@ +TRACEEVENT-CFLAGS diff --git a/kernel/tools/lib/traceevent/Build b/kernel/tools/lib/traceevent/Build new file mode 100644 index 000000000..c681d0575 --- /dev/null +++ b/kernel/tools/lib/traceevent/Build @@ -0,0 +1,17 @@ +libtraceevent-y += event-parse.o +libtraceevent-y += event-plugin.o +libtraceevent-y += trace-seq.o +libtraceevent-y += parse-filter.o +libtraceevent-y += parse-utils.o +libtraceevent-y += kbuffer-parse.o + +plugin_jbd2-y += plugin_jbd2.o +plugin_hrtimer-y += plugin_hrtimer.o +plugin_kmem-y += plugin_kmem.o +plugin_kvm-y += plugin_kvm.o +plugin_mac80211-y += plugin_mac80211.o +plugin_sched_switch-y += plugin_sched_switch.o +plugin_function-y += plugin_function.o +plugin_xen-y += plugin_xen.o +plugin_scsi-y += plugin_scsi.o +plugin_cfg80211-y += plugin_cfg80211.o diff --git a/kernel/tools/lib/traceevent/Makefile b/kernel/tools/lib/traceevent/Makefile new file mode 100644 index 000000000..d410da335 --- /dev/null +++ b/kernel/tools/lib/traceevent/Makefile @@ -0,0 +1,261 @@ +# trace-cmd version +EP_VERSION = 1 +EP_PATCHLEVEL = 1 +EP_EXTRAVERSION = 0 + +# file format version +FILE_VERSION = 6 + +MAKEFLAGS += --no-print-directory + + +# Makefiles suck: This macro sets a default value of $(2) for the +# variable named by $(1), unless the variable has been set by +# environment or command line. This is necessary for CC and AR +# because make sets default values, so the simpler ?= approach +# won't work as expected. +define allow-override + $(if $(or $(findstring environment,$(origin $(1))),\ + $(findstring command line,$(origin $(1)))),,\ + $(eval $(1) = $(2))) +endef + +# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix. +$(call allow-override,CC,$(CROSS_COMPILE)gcc) +$(call allow-override,AR,$(CROSS_COMPILE)ar) + +EXT = -std=gnu99 +INSTALL = install + +# Use DESTDIR for installing into a different root directory. +# This is useful for building a package. The program will be +# installed in this directory as if it was the root directory. +# Then the build tool can move it later. +DESTDIR ?= +DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' + +prefix ?= /usr/local +bindir_relative = bin +bindir = $(prefix)/$(bindir_relative) +man_dir = $(prefix)/share/man +man_dir_SQ = '$(subst ','\'',$(man_dir))' + +export man_dir man_dir_SQ INSTALL +export DESTDIR DESTDIR_SQ + +set_plugin_dir := 1 + +# Set plugin_dir to preffered global plugin location +# If we install under $HOME directory we go under +# $(HOME)/.traceevent/plugins +# +# We dont set PLUGIN_DIR in case we install under $HOME +# directory, because by default the code looks under: +# $(HOME)/.traceevent/plugins by default. +# +ifeq ($(plugin_dir),) +ifeq ($(prefix),$(HOME)) +override plugin_dir = $(HOME)/.traceevent/plugins +set_plugin_dir := 0 +else +override plugin_dir = $(prefix)/lib/traceevent/plugins +endif +endif + +ifeq ($(set_plugin_dir),1) +PLUGIN_DIR = -DPLUGIN_DIR="$(plugin_dir)" +PLUGIN_DIR_SQ = '$(subst ','\'',$(PLUGIN_DIR))' +endif + +include ../../scripts/Makefile.include + +# copy a bit from Linux kbuild + +ifeq ("$(origin V)", "command line") + VERBOSE = $(V) +endif +ifndef VERBOSE + VERBOSE = 0 +endif + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(shell pwd))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +#$(info Determined 'srctree' to be $(srctree)) +endif + +export prefix bindir src obj + +# Shell quotes +bindir_SQ = $(subst ','\'',$(bindir)) +bindir_relative_SQ = $(subst ','\'',$(bindir_relative)) +plugin_dir_SQ = $(subst ','\'',$(plugin_dir)) + +LIB_FILE = libtraceevent.a libtraceevent.so + +CONFIG_INCLUDES = +CONFIG_LIBS = +CONFIG_FLAGS = + +VERSION = $(EP_VERSION) +PATCHLEVEL = $(EP_PATCHLEVEL) +EXTRAVERSION = $(EP_EXTRAVERSION) + +OBJ = $@ +N = + +EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION) + +INCLUDES = -I. -I $(srctree)/tools/include $(CONFIG_INCLUDES) + +# Set compile option CFLAGS +ifdef EXTRA_CFLAGS + CFLAGS := $(EXTRA_CFLAGS) +else + CFLAGS := -g -Wall +endif + +# Append required CFLAGS +override CFLAGS += -fPIC +override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ) +override CFLAGS += $(udis86-flags) -D_GNU_SOURCE + +ifeq ($(VERBOSE),1) + Q = +else + Q = @ +endif + +# Disable command line variables (CFLAGS) overide from top +# level Makefile (perf), otherwise build Makefile will get +# the same command line setup. +MAKEOVERRIDES= + +export srctree OUTPUT CC LD CFLAGS V +build := -f $(srctree)/tools/build/Makefile.build dir=. obj + +PLUGINS = plugin_jbd2.so +PLUGINS += plugin_hrtimer.so +PLUGINS += plugin_kmem.so +PLUGINS += plugin_kvm.so +PLUGINS += plugin_mac80211.so +PLUGINS += plugin_sched_switch.so +PLUGINS += plugin_function.so +PLUGINS += plugin_xen.so +PLUGINS += plugin_scsi.so +PLUGINS += plugin_cfg80211.so + +PLUGINS := $(addprefix $(OUTPUT),$(PLUGINS)) +PLUGINS_IN := $(PLUGINS:.so=-in.o) + +TE_IN := $(OUTPUT)libtraceevent-in.o +LIB_FILE := $(addprefix $(OUTPUT),$(LIB_FILE)) + +CMD_TARGETS = $(LIB_FILE) $(PLUGINS) + +TARGETS = $(CMD_TARGETS) + +all: all_cmd + +all_cmd: $(CMD_TARGETS) + +$(TE_IN): force + $(Q)$(MAKE) $(build)=libtraceevent + +$(OUTPUT)libtraceevent.so: $(TE_IN) + $(QUIET_LINK)$(CC) --shared $^ -o $@ + +$(OUTPUT)libtraceevent.a: $(TE_IN) + $(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^ + +plugins: $(PLUGINS) + +__plugin_obj = $(notdir $@) + plugin_obj = $(__plugin_obj:-in.o=) + +$(PLUGINS_IN): force + $(Q)$(MAKE) $(build)=$(plugin_obj) + +$(OUTPUT)%.so: $(OUTPUT)%-in.o + $(QUIET_LINK)$(CC) $(CFLAGS) -shared -nostartfiles -o $@ $^ + +define make_version.h + (echo '/* This file is automatically generated. Do not modify. */'; \ + echo \#define VERSION_CODE $(shell \ + expr $(VERSION) \* 256 + $(PATCHLEVEL)); \ + echo '#define EXTRAVERSION ' $(EXTRAVERSION); \ + echo '#define VERSION_STRING "'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'"'; \ + echo '#define FILE_VERSION '$(FILE_VERSION); \ + ) > $1 +endef + +define update_version.h + ($(call make_version.h, $@.tmp); \ + if [ -r $@ ] && cmp -s $@ $@.tmp; then \ + rm -f $@.tmp; \ + else \ + echo ' UPDATE $@'; \ + mv -f $@.tmp $@; \ + fi); +endef + +ep_version.h: force + $(Q)$(N)$(call update_version.h) + +VERSION_FILES = ep_version.h + +define update_dir + (echo $1 > $@.tmp; \ + if [ -r $@ ] && cmp -s $@ $@.tmp; then \ + rm -f $@.tmp; \ + else \ + echo ' UPDATE $@'; \ + mv -f $@.tmp $@; \ + fi); +endef + +tags: force + $(RM) tags + find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px \ + --regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/' + +TAGS: force + $(RM) TAGS + find . -name '*.[ch]' | xargs etags \ + --regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/' + +define do_install + if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ + fi; \ + $(INSTALL) $1 '$(DESTDIR_SQ)$2' +endef + +define do_install_plugins + for plugin in $1; do \ + $(call do_install,$$plugin,$(plugin_dir_SQ)); \ + done +endef + +install_lib: all_cmd install_plugins + $(call QUIET_INSTALL, $(LIB_FILE)) \ + $(call do_install,$(LIB_FILE),$(bindir_SQ)) + +install_plugins: $(PLUGINS) + $(call QUIET_INSTALL, trace_plugins) \ + $(call do_install_plugins, $(PLUGINS)) + +install: install_lib + +clean: + $(call QUIET_CLEAN, libtraceevent) \ + $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \ + $(RM) TRACEEVENT-CFLAGS tags TAGS + +PHONY += force plugins +force: + +# Declare the contents of the .PHONY variable as phony. We keep that +# information in a variable so we can use it in if_changed and friends. +.PHONY: $(PHONY) diff --git a/kernel/tools/lib/traceevent/event-parse.c b/kernel/tools/lib/traceevent/event-parse.c new file mode 100644 index 000000000..29f94f6f0 --- /dev/null +++ b/kernel/tools/lib/traceevent/event-parse.c @@ -0,0 +1,6572 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * The parts for function graph printing was taken and modified from the + * Linux Kernel that were written by + * - Copyright (C) 2009 Frederic Weisbecker, + * Frederic Weisbecker gave his permission to relicense the code to + * the Lesser General Public License. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "event-parse.h" +#include "event-utils.h" + +static const char *input_buf; +static unsigned long long input_buf_ptr; +static unsigned long long input_buf_siz; + +static int is_flag_field; +static int is_symbolic_field; + +static int show_warning = 1; + +#define do_warning(fmt, ...) \ + do { \ + if (show_warning) \ + warning(fmt, ##__VA_ARGS__); \ + } while (0) + +#define do_warning_event(event, fmt, ...) \ + do { \ + if (!show_warning) \ + continue; \ + \ + if (event) \ + warning("[%s:%s] " fmt, event->system, \ + event->name, ##__VA_ARGS__); \ + else \ + warning(fmt, ##__VA_ARGS__); \ + } while (0) + +static void init_input_buf(const char *buf, unsigned long long size) +{ + input_buf = buf; + input_buf_siz = size; + input_buf_ptr = 0; +} + +const char *pevent_get_input_buf(void) +{ + return input_buf; +} + +unsigned long long pevent_get_input_buf_ptr(void) +{ + return input_buf_ptr; +} + +struct event_handler { + struct event_handler *next; + int id; + const char *sys_name; + const char *event_name; + pevent_event_handler_func func; + void *context; +}; + +struct pevent_func_params { + struct pevent_func_params *next; + enum pevent_func_arg_type type; +}; + +struct pevent_function_handler { + struct pevent_function_handler *next; + enum pevent_func_arg_type ret_type; + char *name; + pevent_func_handler func; + struct pevent_func_params *params; + int nr_args; +}; + +static unsigned long long +process_defined_func(struct trace_seq *s, void *data, int size, + struct event_format *event, struct print_arg *arg); + +static void free_func_handle(struct pevent_function_handler *func); + +/** + * pevent_buffer_init - init buffer for parsing + * @buf: buffer to parse + * @size: the size of the buffer + * + * For use with pevent_read_token(), this initializes the internal + * buffer that pevent_read_token() will parse. + */ +void pevent_buffer_init(const char *buf, unsigned long long size) +{ + init_input_buf(buf, size); +} + +void breakpoint(void) +{ + static int x; + x++; +} + +struct print_arg *alloc_arg(void) +{ + return calloc(1, sizeof(struct print_arg)); +} + +struct cmdline { + char *comm; + int pid; +}; + +static int cmdline_cmp(const void *a, const void *b) +{ + const struct cmdline *ca = a; + const struct cmdline *cb = b; + + if (ca->pid < cb->pid) + return -1; + if (ca->pid > cb->pid) + return 1; + + return 0; +} + +struct cmdline_list { + struct cmdline_list *next; + char *comm; + int pid; +}; + +static int cmdline_init(struct pevent *pevent) +{ + struct cmdline_list *cmdlist = pevent->cmdlist; + struct cmdline_list *item; + struct cmdline *cmdlines; + int i; + + cmdlines = malloc(sizeof(*cmdlines) * pevent->cmdline_count); + if (!cmdlines) + return -1; + + i = 0; + while (cmdlist) { + cmdlines[i].pid = cmdlist->pid; + cmdlines[i].comm = cmdlist->comm; + i++; + item = cmdlist; + cmdlist = cmdlist->next; + free(item); + } + + qsort(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp); + + pevent->cmdlines = cmdlines; + pevent->cmdlist = NULL; + + return 0; +} + +static const char *find_cmdline(struct pevent *pevent, int pid) +{ + const struct cmdline *comm; + struct cmdline key; + + if (!pid) + return ""; + + if (!pevent->cmdlines && cmdline_init(pevent)) + return ""; + + key.pid = pid; + + comm = bsearch(&key, pevent->cmdlines, pevent->cmdline_count, + sizeof(*pevent->cmdlines), cmdline_cmp); + + if (comm) + return comm->comm; + return "<...>"; +} + +/** + * pevent_pid_is_registered - return if a pid has a cmdline registered + * @pevent: handle for the pevent + * @pid: The pid to check if it has a cmdline registered with. + * + * Returns 1 if the pid has a cmdline mapped to it + * 0 otherwise. + */ +int pevent_pid_is_registered(struct pevent *pevent, int pid) +{ + const struct cmdline *comm; + struct cmdline key; + + if (!pid) + return 1; + + if (!pevent->cmdlines && cmdline_init(pevent)) + return 0; + + key.pid = pid; + + comm = bsearch(&key, pevent->cmdlines, pevent->cmdline_count, + sizeof(*pevent->cmdlines), cmdline_cmp); + + if (comm) + return 1; + return 0; +} + +/* + * If the command lines have been converted to an array, then + * we must add this pid. This is much slower than when cmdlines + * are added before the array is initialized. + */ +static int add_new_comm(struct pevent *pevent, const char *comm, int pid) +{ + struct cmdline *cmdlines = pevent->cmdlines; + const struct cmdline *cmdline; + struct cmdline key; + + if (!pid) + return 0; + + /* avoid duplicates */ + key.pid = pid; + + cmdline = bsearch(&key, pevent->cmdlines, pevent->cmdline_count, + sizeof(*pevent->cmdlines), cmdline_cmp); + if (cmdline) { + errno = EEXIST; + return -1; + } + + cmdlines = realloc(cmdlines, sizeof(*cmdlines) * (pevent->cmdline_count + 1)); + if (!cmdlines) { + errno = ENOMEM; + return -1; + } + + cmdlines[pevent->cmdline_count].comm = strdup(comm); + if (!cmdlines[pevent->cmdline_count].comm) { + free(cmdlines); + errno = ENOMEM; + return -1; + } + + cmdlines[pevent->cmdline_count].pid = pid; + + if (cmdlines[pevent->cmdline_count].comm) + pevent->cmdline_count++; + + qsort(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp); + pevent->cmdlines = cmdlines; + + return 0; +} + +/** + * pevent_register_comm - register a pid / comm mapping + * @pevent: handle for the pevent + * @comm: the command line to register + * @pid: the pid to map the command line to + * + * This adds a mapping to search for command line names with + * a given pid. The comm is duplicated. + */ +int pevent_register_comm(struct pevent *pevent, const char *comm, int pid) +{ + struct cmdline_list *item; + + if (pevent->cmdlines) + return add_new_comm(pevent, comm, pid); + + item = malloc(sizeof(*item)); + if (!item) + return -1; + + if (comm) + item->comm = strdup(comm); + else + item->comm = strdup("<...>"); + if (!item->comm) { + free(item); + return -1; + } + item->pid = pid; + item->next = pevent->cmdlist; + + pevent->cmdlist = item; + pevent->cmdline_count++; + + return 0; +} + +int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock) +{ + pevent->trace_clock = strdup(trace_clock); + if (!pevent->trace_clock) { + errno = ENOMEM; + return -1; + } + return 0; +} + +struct func_map { + unsigned long long addr; + char *func; + char *mod; +}; + +struct func_list { + struct func_list *next; + unsigned long long addr; + char *func; + char *mod; +}; + +static int func_cmp(const void *a, const void *b) +{ + const struct func_map *fa = a; + const struct func_map *fb = b; + + if (fa->addr < fb->addr) + return -1; + if (fa->addr > fb->addr) + return 1; + + return 0; +} + +/* + * We are searching for a record in between, not an exact + * match. + */ +static int func_bcmp(const void *a, const void *b) +{ + const struct func_map *fa = a; + const struct func_map *fb = b; + + if ((fa->addr == fb->addr) || + + (fa->addr > fb->addr && + fa->addr < (fb+1)->addr)) + return 0; + + if (fa->addr < fb->addr) + return -1; + + return 1; +} + +static int func_map_init(struct pevent *pevent) +{ + struct func_list *funclist; + struct func_list *item; + struct func_map *func_map; + int i; + + func_map = malloc(sizeof(*func_map) * (pevent->func_count + 1)); + if (!func_map) + return -1; + + funclist = pevent->funclist; + + i = 0; + while (funclist) { + func_map[i].func = funclist->func; + func_map[i].addr = funclist->addr; + func_map[i].mod = funclist->mod; + i++; + item = funclist; + funclist = funclist->next; + free(item); + } + + qsort(func_map, pevent->func_count, sizeof(*func_map), func_cmp); + + /* + * Add a special record at the end. + */ + func_map[pevent->func_count].func = NULL; + func_map[pevent->func_count].addr = 0; + func_map[pevent->func_count].mod = NULL; + + pevent->func_map = func_map; + pevent->funclist = NULL; + + return 0; +} + +static struct func_map * +find_func(struct pevent *pevent, unsigned long long addr) +{ + struct func_map *func; + struct func_map key; + + if (!pevent->func_map) + func_map_init(pevent); + + key.addr = addr; + + func = bsearch(&key, pevent->func_map, pevent->func_count, + sizeof(*pevent->func_map), func_bcmp); + + return func; +} + +/** + * pevent_find_function - find a function by a given address + * @pevent: handle for the pevent + * @addr: the address to find the function with + * + * Returns a pointer to the function stored that has the given + * address. Note, the address does not have to be exact, it + * will select the function that would contain the address. + */ +const char *pevent_find_function(struct pevent *pevent, unsigned long long addr) +{ + struct func_map *map; + + map = find_func(pevent, addr); + if (!map) + return NULL; + + return map->func; +} + +/** + * pevent_find_function_address - find a function address by a given address + * @pevent: handle for the pevent + * @addr: the address to find the function with + * + * Returns the address the function starts at. This can be used in + * conjunction with pevent_find_function to print both the function + * name and the function offset. + */ +unsigned long long +pevent_find_function_address(struct pevent *pevent, unsigned long long addr) +{ + struct func_map *map; + + map = find_func(pevent, addr); + if (!map) + return 0; + + return map->addr; +} + +/** + * pevent_register_function - register a function with a given address + * @pevent: handle for the pevent + * @function: the function name to register + * @addr: the address the function starts at + * @mod: the kernel module the function may be in (NULL for none) + * + * This registers a function name with an address and module. + * The @func passed in is duplicated. + */ +int pevent_register_function(struct pevent *pevent, char *func, + unsigned long long addr, char *mod) +{ + struct func_list *item = malloc(sizeof(*item)); + + if (!item) + return -1; + + item->next = pevent->funclist; + item->func = strdup(func); + if (!item->func) + goto out_free; + + if (mod) { + item->mod = strdup(mod); + if (!item->mod) + goto out_free_func; + } else + item->mod = NULL; + item->addr = addr; + + pevent->funclist = item; + pevent->func_count++; + + return 0; + +out_free_func: + free(item->func); + item->func = NULL; +out_free: + free(item); + errno = ENOMEM; + return -1; +} + +/** + * pevent_print_funcs - print out the stored functions + * @pevent: handle for the pevent + * + * This prints out the stored functions. + */ +void pevent_print_funcs(struct pevent *pevent) +{ + int i; + + if (!pevent->func_map) + func_map_init(pevent); + + for (i = 0; i < (int)pevent->func_count; i++) { + printf("%016llx %s", + pevent->func_map[i].addr, + pevent->func_map[i].func); + if (pevent->func_map[i].mod) + printf(" [%s]\n", pevent->func_map[i].mod); + else + printf("\n"); + } +} + +struct printk_map { + unsigned long long addr; + char *printk; +}; + +struct printk_list { + struct printk_list *next; + unsigned long long addr; + char *printk; +}; + +static int printk_cmp(const void *a, const void *b) +{ + const struct printk_map *pa = a; + const struct printk_map *pb = b; + + if (pa->addr < pb->addr) + return -1; + if (pa->addr > pb->addr) + return 1; + + return 0; +} + +static int printk_map_init(struct pevent *pevent) +{ + struct printk_list *printklist; + struct printk_list *item; + struct printk_map *printk_map; + int i; + + printk_map = malloc(sizeof(*printk_map) * (pevent->printk_count + 1)); + if (!printk_map) + return -1; + + printklist = pevent->printklist; + + i = 0; + while (printklist) { + printk_map[i].printk = printklist->printk; + printk_map[i].addr = printklist->addr; + i++; + item = printklist; + printklist = printklist->next; + free(item); + } + + qsort(printk_map, pevent->printk_count, sizeof(*printk_map), printk_cmp); + + pevent->printk_map = printk_map; + pevent->printklist = NULL; + + return 0; +} + +static struct printk_map * +find_printk(struct pevent *pevent, unsigned long long addr) +{ + struct printk_map *printk; + struct printk_map key; + + if (!pevent->printk_map && printk_map_init(pevent)) + return NULL; + + key.addr = addr; + + printk = bsearch(&key, pevent->printk_map, pevent->printk_count, + sizeof(*pevent->printk_map), printk_cmp); + + return printk; +} + +/** + * pevent_register_print_string - register a string by its address + * @pevent: handle for the pevent + * @fmt: the string format to register + * @addr: the address the string was located at + * + * This registers a string by the address it was stored in the kernel. + * The @fmt passed in is duplicated. + */ +int pevent_register_print_string(struct pevent *pevent, const char *fmt, + unsigned long long addr) +{ + struct printk_list *item = malloc(sizeof(*item)); + char *p; + + if (!item) + return -1; + + item->next = pevent->printklist; + item->addr = addr; + + /* Strip off quotes and '\n' from the end */ + if (fmt[0] == '"') + fmt++; + item->printk = strdup(fmt); + if (!item->printk) + goto out_free; + + p = item->printk + strlen(item->printk) - 1; + if (*p == '"') + *p = 0; + + p -= 2; + if (strcmp(p, "\\n") == 0) + *p = 0; + + pevent->printklist = item; + pevent->printk_count++; + + return 0; + +out_free: + free(item); + errno = ENOMEM; + return -1; +} + +/** + * pevent_print_printk - print out the stored strings + * @pevent: handle for the pevent + * + * This prints the string formats that were stored. + */ +void pevent_print_printk(struct pevent *pevent) +{ + int i; + + if (!pevent->printk_map) + printk_map_init(pevent); + + for (i = 0; i < (int)pevent->printk_count; i++) { + printf("%016llx %s\n", + pevent->printk_map[i].addr, + pevent->printk_map[i].printk); + } +} + +static struct event_format *alloc_event(void) +{ + return calloc(1, sizeof(struct event_format)); +} + +static int add_event(struct pevent *pevent, struct event_format *event) +{ + int i; + struct event_format **events = realloc(pevent->events, sizeof(event) * + (pevent->nr_events + 1)); + if (!events) + return -1; + + pevent->events = events; + + for (i = 0; i < pevent->nr_events; i++) { + if (pevent->events[i]->id > event->id) + break; + } + if (i < pevent->nr_events) + memmove(&pevent->events[i + 1], + &pevent->events[i], + sizeof(event) * (pevent->nr_events - i)); + + pevent->events[i] = event; + pevent->nr_events++; + + event->pevent = pevent; + + return 0; +} + +static int event_item_type(enum event_type type) +{ + switch (type) { + case EVENT_ITEM ... EVENT_SQUOTE: + return 1; + case EVENT_ERROR ... EVENT_DELIM: + default: + return 0; + } +} + +static void free_flag_sym(struct print_flag_sym *fsym) +{ + struct print_flag_sym *next; + + while (fsym) { + next = fsym->next; + free(fsym->value); + free(fsym->str); + free(fsym); + fsym = next; + } +} + +static void free_arg(struct print_arg *arg) +{ + struct print_arg *farg; + + if (!arg) + return; + + switch (arg->type) { + case PRINT_ATOM: + free(arg->atom.atom); + break; + case PRINT_FIELD: + free(arg->field.name); + break; + case PRINT_FLAGS: + free_arg(arg->flags.field); + free(arg->flags.delim); + free_flag_sym(arg->flags.flags); + break; + case PRINT_SYMBOL: + free_arg(arg->symbol.field); + free_flag_sym(arg->symbol.symbols); + break; + case PRINT_HEX: + free_arg(arg->hex.field); + free_arg(arg->hex.size); + break; + case PRINT_INT_ARRAY: + free_arg(arg->int_array.field); + free_arg(arg->int_array.count); + free_arg(arg->int_array.el_size); + break; + case PRINT_TYPE: + free(arg->typecast.type); + free_arg(arg->typecast.item); + break; + case PRINT_STRING: + case PRINT_BSTRING: + free(arg->string.string); + break; + case PRINT_BITMASK: + free(arg->bitmask.bitmask); + break; + case PRINT_DYNAMIC_ARRAY: + free(arg->dynarray.index); + break; + case PRINT_OP: + free(arg->op.op); + free_arg(arg->op.left); + free_arg(arg->op.right); + break; + case PRINT_FUNC: + while (arg->func.args) { + farg = arg->func.args; + arg->func.args = farg->next; + free_arg(farg); + } + break; + + case PRINT_NULL: + default: + break; + } + + free(arg); +} + +static enum event_type get_type(int ch) +{ + if (ch == '\n') + return EVENT_NEWLINE; + if (isspace(ch)) + return EVENT_SPACE; + if (isalnum(ch) || ch == '_') + return EVENT_ITEM; + if (ch == '\'') + return EVENT_SQUOTE; + if (ch == '"') + return EVENT_DQUOTE; + if (!isprint(ch)) + return EVENT_NONE; + if (ch == '(' || ch == ')' || ch == ',') + return EVENT_DELIM; + + return EVENT_OP; +} + +static int __read_char(void) +{ + if (input_buf_ptr >= input_buf_siz) + return -1; + + return input_buf[input_buf_ptr++]; +} + +static int __peek_char(void) +{ + if (input_buf_ptr >= input_buf_siz) + return -1; + + return input_buf[input_buf_ptr]; +} + +/** + * pevent_peek_char - peek at the next character that will be read + * + * Returns the next character read, or -1 if end of buffer. + */ +int pevent_peek_char(void) +{ + return __peek_char(); +} + +static int extend_token(char **tok, char *buf, int size) +{ + char *newtok = realloc(*tok, size); + + if (!newtok) { + free(*tok); + *tok = NULL; + return -1; + } + + if (!*tok) + strcpy(newtok, buf); + else + strcat(newtok, buf); + *tok = newtok; + + return 0; +} + +static enum event_type force_token(const char *str, char **tok); + +static enum event_type __read_token(char **tok) +{ + char buf[BUFSIZ]; + int ch, last_ch, quote_ch, next_ch; + int i = 0; + int tok_size = 0; + enum event_type type; + + *tok = NULL; + + + ch = __read_char(); + if (ch < 0) + return EVENT_NONE; + + type = get_type(ch); + if (type == EVENT_NONE) + return type; + + buf[i++] = ch; + + switch (type) { + case EVENT_NEWLINE: + case EVENT_DELIM: + if (asprintf(tok, "%c", ch) < 0) + return EVENT_ERROR; + + return type; + + case EVENT_OP: + switch (ch) { + case '-': + next_ch = __peek_char(); + if (next_ch == '>') { + buf[i++] = __read_char(); + break; + } + /* fall through */ + case '+': + case '|': + case '&': + case '>': + case '<': + last_ch = ch; + ch = __peek_char(); + if (ch != last_ch) + goto test_equal; + buf[i++] = __read_char(); + switch (last_ch) { + case '>': + case '<': + goto test_equal; + default: + break; + } + break; + case '!': + case '=': + goto test_equal; + default: /* what should we do instead? */ + break; + } + buf[i] = 0; + *tok = strdup(buf); + return type; + + test_equal: + ch = __peek_char(); + if (ch == '=') + buf[i++] = __read_char(); + goto out; + + case EVENT_DQUOTE: + case EVENT_SQUOTE: + /* don't keep quotes */ + i--; + quote_ch = ch; + last_ch = 0; + concat: + do { + if (i == (BUFSIZ - 1)) { + buf[i] = 0; + tok_size += BUFSIZ; + + if (extend_token(tok, buf, tok_size) < 0) + return EVENT_NONE; + i = 0; + } + last_ch = ch; + ch = __read_char(); + buf[i++] = ch; + /* the '\' '\' will cancel itself */ + if (ch == '\\' && last_ch == '\\') + last_ch = 0; + } while (ch != quote_ch || last_ch == '\\'); + /* remove the last quote */ + i--; + + /* + * For strings (double quotes) check the next token. + * If it is another string, concatinate the two. + */ + if (type == EVENT_DQUOTE) { + unsigned long long save_input_buf_ptr = input_buf_ptr; + + do { + ch = __read_char(); + } while (isspace(ch)); + if (ch == '"') + goto concat; + input_buf_ptr = save_input_buf_ptr; + } + + goto out; + + case EVENT_ERROR ... EVENT_SPACE: + case EVENT_ITEM: + default: + break; + } + + while (get_type(__peek_char()) == type) { + if (i == (BUFSIZ - 1)) { + buf[i] = 0; + tok_size += BUFSIZ; + + if (extend_token(tok, buf, tok_size) < 0) + return EVENT_NONE; + i = 0; + } + ch = __read_char(); + buf[i++] = ch; + } + + out: + buf[i] = 0; + if (extend_token(tok, buf, tok_size + i + 1) < 0) + return EVENT_NONE; + + if (type == EVENT_ITEM) { + /* + * Older versions of the kernel has a bug that + * creates invalid symbols and will break the mac80211 + * parsing. This is a work around to that bug. + * + * See Linux kernel commit: + * 811cb50baf63461ce0bdb234927046131fc7fa8b + */ + if (strcmp(*tok, "LOCAL_PR_FMT") == 0) { + free(*tok); + *tok = NULL; + return force_token("\"\%s\" ", tok); + } else if (strcmp(*tok, "STA_PR_FMT") == 0) { + free(*tok); + *tok = NULL; + return force_token("\" sta:%pM\" ", tok); + } else if (strcmp(*tok, "VIF_PR_FMT") == 0) { + free(*tok); + *tok = NULL; + return force_token("\" vif:%p(%d)\" ", tok); + } + } + + return type; +} + +static enum event_type force_token(const char *str, char **tok) +{ + const char *save_input_buf; + unsigned long long save_input_buf_ptr; + unsigned long long save_input_buf_siz; + enum event_type type; + + /* save off the current input pointers */ + save_input_buf = input_buf; + save_input_buf_ptr = input_buf_ptr; + save_input_buf_siz = input_buf_siz; + + init_input_buf(str, strlen(str)); + + type = __read_token(tok); + + /* reset back to original token */ + input_buf = save_input_buf; + input_buf_ptr = save_input_buf_ptr; + input_buf_siz = save_input_buf_siz; + + return type; +} + +static void free_token(char *tok) +{ + if (tok) + free(tok); +} + +static enum event_type read_token(char **tok) +{ + enum event_type type; + + for (;;) { + type = __read_token(tok); + if (type != EVENT_SPACE) + return type; + + free_token(*tok); + } + + /* not reached */ + *tok = NULL; + return EVENT_NONE; +} + +/** + * pevent_read_token - access to utilites to use the pevent parser + * @tok: The token to return + * + * This will parse tokens from the string given by + * pevent_init_data(). + * + * Returns the token type. + */ +enum event_type pevent_read_token(char **tok) +{ + return read_token(tok); +} + +/** + * pevent_free_token - free a token returned by pevent_read_token + * @token: the token to free + */ +void pevent_free_token(char *token) +{ + free_token(token); +} + +/* no newline */ +static enum event_type read_token_item(char **tok) +{ + enum event_type type; + + for (;;) { + type = __read_token(tok); + if (type != EVENT_SPACE && type != EVENT_NEWLINE) + return type; + free_token(*tok); + *tok = NULL; + } + + /* not reached */ + *tok = NULL; + return EVENT_NONE; +} + +static int test_type(enum event_type type, enum event_type expect) +{ + if (type != expect) { + do_warning("Error: expected type %d but read %d", + expect, type); + return -1; + } + return 0; +} + +static int test_type_token(enum event_type type, const char *token, + enum event_type expect, const char *expect_tok) +{ + if (type != expect) { + do_warning("Error: expected type %d but read %d", + expect, type); + return -1; + } + + if (strcmp(token, expect_tok) != 0) { + do_warning("Error: expected '%s' but read '%s'", + expect_tok, token); + return -1; + } + return 0; +} + +static int __read_expect_type(enum event_type expect, char **tok, int newline_ok) +{ + enum event_type type; + + if (newline_ok) + type = read_token(tok); + else + type = read_token_item(tok); + return test_type(type, expect); +} + +static int read_expect_type(enum event_type expect, char **tok) +{ + return __read_expect_type(expect, tok, 1); +} + +static int __read_expected(enum event_type expect, const char *str, + int newline_ok) +{ + enum event_type type; + char *token; + int ret; + + if (newline_ok) + type = read_token(&token); + else + type = read_token_item(&token); + + ret = test_type_token(type, token, expect, str); + + free_token(token); + + return ret; +} + +static int read_expected(enum event_type expect, const char *str) +{ + return __read_expected(expect, str, 1); +} + +static int read_expected_item(enum event_type expect, const char *str) +{ + return __read_expected(expect, str, 0); +} + +static char *event_read_name(void) +{ + char *token; + + if (read_expected(EVENT_ITEM, "name") < 0) + return NULL; + + if (read_expected(EVENT_OP, ":") < 0) + return NULL; + + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + + return token; + + fail: + free_token(token); + return NULL; +} + +static int event_read_id(void) +{ + char *token; + int id; + + if (read_expected_item(EVENT_ITEM, "ID") < 0) + return -1; + + if (read_expected(EVENT_OP, ":") < 0) + return -1; + + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + + id = strtoul(token, NULL, 0); + free_token(token); + return id; + + fail: + free_token(token); + return -1; +} + +static int field_is_string(struct format_field *field) +{ + if ((field->flags & FIELD_IS_ARRAY) && + (strstr(field->type, "char") || strstr(field->type, "u8") || + strstr(field->type, "s8"))) + return 1; + + return 0; +} + +static int field_is_dynamic(struct format_field *field) +{ + if (strncmp(field->type, "__data_loc", 10) == 0) + return 1; + + return 0; +} + +static int field_is_long(struct format_field *field) +{ + /* includes long long */ + if (strstr(field->type, "long")) + return 1; + + return 0; +} + +static unsigned int type_size(const char *name) +{ + /* This covers all FIELD_IS_STRING types. */ + static struct { + const char *type; + unsigned int size; + } table[] = { + { "u8", 1 }, + { "u16", 2 }, + { "u32", 4 }, + { "u64", 8 }, + { "s8", 1 }, + { "s16", 2 }, + { "s32", 4 }, + { "s64", 8 }, + { "char", 1 }, + { }, + }; + int i; + + for (i = 0; table[i].type; i++) { + if (!strcmp(table[i].type, name)) + return table[i].size; + } + + return 0; +} + +static int event_read_fields(struct event_format *event, struct format_field **fields) +{ + struct format_field *field = NULL; + enum event_type type; + char *token; + char *last_token; + int count = 0; + + do { + unsigned int size_dynamic = 0; + + type = read_token(&token); + if (type == EVENT_NEWLINE) { + free_token(token); + return count; + } + + count++; + + if (test_type_token(type, token, EVENT_ITEM, "field")) + goto fail; + free_token(token); + + type = read_token(&token); + /* + * The ftrace fields may still use the "special" name. + * Just ignore it. + */ + if (event->flags & EVENT_FL_ISFTRACE && + type == EVENT_ITEM && strcmp(token, "special") == 0) { + free_token(token); + type = read_token(&token); + } + + if (test_type_token(type, token, EVENT_OP, ":") < 0) + goto fail; + + free_token(token); + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + + last_token = token; + + field = calloc(1, sizeof(*field)); + if (!field) + goto fail; + + field->event = event; + + /* read the rest of the type */ + for (;;) { + type = read_token(&token); + if (type == EVENT_ITEM || + (type == EVENT_OP && strcmp(token, "*") == 0) || + /* + * Some of the ftrace fields are broken and have + * an illegal "." in them. + */ + (event->flags & EVENT_FL_ISFTRACE && + type == EVENT_OP && strcmp(token, ".") == 0)) { + + if (strcmp(token, "*") == 0) + field->flags |= FIELD_IS_POINTER; + + if (field->type) { + char *new_type; + new_type = realloc(field->type, + strlen(field->type) + + strlen(last_token) + 2); + if (!new_type) { + free(last_token); + goto fail; + } + field->type = new_type; + strcat(field->type, " "); + strcat(field->type, last_token); + free(last_token); + } else + field->type = last_token; + last_token = token; + continue; + } + + break; + } + + if (!field->type) { + do_warning_event(event, "%s: no type found", __func__); + goto fail; + } + field->name = last_token; + + if (test_type(type, EVENT_OP)) + goto fail; + + if (strcmp(token, "[") == 0) { + enum event_type last_type = type; + char *brackets = token; + char *new_brackets; + int len; + + field->flags |= FIELD_IS_ARRAY; + + type = read_token(&token); + + if (type == EVENT_ITEM) + field->arraylen = strtoul(token, NULL, 0); + else + field->arraylen = 0; + + while (strcmp(token, "]") != 0) { + if (last_type == EVENT_ITEM && + type == EVENT_ITEM) + len = 2; + else + len = 1; + last_type = type; + + new_brackets = realloc(brackets, + strlen(brackets) + + strlen(token) + len); + if (!new_brackets) { + free(brackets); + goto fail; + } + brackets = new_brackets; + if (len == 2) + strcat(brackets, " "); + strcat(brackets, token); + /* We only care about the last token */ + field->arraylen = strtoul(token, NULL, 0); + free_token(token); + type = read_token(&token); + if (type == EVENT_NONE) { + do_warning_event(event, "failed to find token"); + goto fail; + } + } + + free_token(token); + + new_brackets = realloc(brackets, strlen(brackets) + 2); + if (!new_brackets) { + free(brackets); + goto fail; + } + brackets = new_brackets; + strcat(brackets, "]"); + + /* add brackets to type */ + + type = read_token(&token); + /* + * If the next token is not an OP, then it is of + * the format: type [] item; + */ + if (type == EVENT_ITEM) { + char *new_type; + new_type = realloc(field->type, + strlen(field->type) + + strlen(field->name) + + strlen(brackets) + 2); + if (!new_type) { + free(brackets); + goto fail; + } + field->type = new_type; + strcat(field->type, " "); + strcat(field->type, field->name); + size_dynamic = type_size(field->name); + free_token(field->name); + strcat(field->type, brackets); + field->name = token; + type = read_token(&token); + } else { + char *new_type; + new_type = realloc(field->type, + strlen(field->type) + + strlen(brackets) + 1); + if (!new_type) { + free(brackets); + goto fail; + } + field->type = new_type; + strcat(field->type, brackets); + } + free(brackets); + } + + if (field_is_string(field)) + field->flags |= FIELD_IS_STRING; + if (field_is_dynamic(field)) + field->flags |= FIELD_IS_DYNAMIC; + if (field_is_long(field)) + field->flags |= FIELD_IS_LONG; + + if (test_type_token(type, token, EVENT_OP, ";")) + goto fail; + free_token(token); + + if (read_expected(EVENT_ITEM, "offset") < 0) + goto fail_expect; + + if (read_expected(EVENT_OP, ":") < 0) + goto fail_expect; + + if (read_expect_type(EVENT_ITEM, &token)) + goto fail; + field->offset = strtoul(token, NULL, 0); + free_token(token); + + if (read_expected(EVENT_OP, ";") < 0) + goto fail_expect; + + if (read_expected(EVENT_ITEM, "size") < 0) + goto fail_expect; + + if (read_expected(EVENT_OP, ":") < 0) + goto fail_expect; + + if (read_expect_type(EVENT_ITEM, &token)) + goto fail; + field->size = strtoul(token, NULL, 0); + free_token(token); + + if (read_expected(EVENT_OP, ";") < 0) + goto fail_expect; + + type = read_token(&token); + if (type != EVENT_NEWLINE) { + /* newer versions of the kernel have a "signed" type */ + if (test_type_token(type, token, EVENT_ITEM, "signed")) + goto fail; + + free_token(token); + + if (read_expected(EVENT_OP, ":") < 0) + goto fail_expect; + + if (read_expect_type(EVENT_ITEM, &token)) + goto fail; + + if (strtoul(token, NULL, 0)) + field->flags |= FIELD_IS_SIGNED; + + free_token(token); + if (read_expected(EVENT_OP, ";") < 0) + goto fail_expect; + + if (read_expect_type(EVENT_NEWLINE, &token)) + goto fail; + } + + free_token(token); + + if (field->flags & FIELD_IS_ARRAY) { + if (field->arraylen) + field->elementsize = field->size / field->arraylen; + else if (field->flags & FIELD_IS_DYNAMIC) + field->elementsize = size_dynamic; + else if (field->flags & FIELD_IS_STRING) + field->elementsize = 1; + else if (field->flags & FIELD_IS_LONG) + field->elementsize = event->pevent ? + event->pevent->long_size : + sizeof(long); + } else + field->elementsize = field->size; + + *fields = field; + fields = &field->next; + + } while (1); + + return 0; + +fail: + free_token(token); +fail_expect: + if (field) { + free(field->type); + free(field->name); + free(field); + } + return -1; +} + +static int event_read_format(struct event_format *event) +{ + char *token; + int ret; + + if (read_expected_item(EVENT_ITEM, "format") < 0) + return -1; + + if (read_expected(EVENT_OP, ":") < 0) + return -1; + + if (read_expect_type(EVENT_NEWLINE, &token)) + goto fail; + free_token(token); + + ret = event_read_fields(event, &event->format.common_fields); + if (ret < 0) + return ret; + event->format.nr_common = ret; + + ret = event_read_fields(event, &event->format.fields); + if (ret < 0) + return ret; + event->format.nr_fields = ret; + + return 0; + + fail: + free_token(token); + return -1; +} + +static enum event_type +process_arg_token(struct event_format *event, struct print_arg *arg, + char **tok, enum event_type type); + +static enum event_type +process_arg(struct event_format *event, struct print_arg *arg, char **tok) +{ + enum event_type type; + char *token; + + type = read_token(&token); + *tok = token; + + return process_arg_token(event, arg, tok, type); +} + +static enum event_type +process_op(struct event_format *event, struct print_arg *arg, char **tok); + +/* + * For __print_symbolic() and __print_flags, we need to completely + * evaluate the first argument, which defines what to print next. + */ +static enum event_type +process_field_arg(struct event_format *event, struct print_arg *arg, char **tok) +{ + enum event_type type; + + type = process_arg(event, arg, tok); + + while (type == EVENT_OP) { + type = process_op(event, arg, tok); + } + + return type; +} + +static enum event_type +process_cond(struct event_format *event, struct print_arg *top, char **tok) +{ + struct print_arg *arg, *left, *right; + enum event_type type; + char *token = NULL; + + arg = alloc_arg(); + left = alloc_arg(); + right = alloc_arg(); + + if (!arg || !left || !right) { + do_warning_event(event, "%s: not enough memory!", __func__); + /* arg will be freed at out_free */ + free_arg(left); + free_arg(right); + goto out_free; + } + + arg->type = PRINT_OP; + arg->op.left = left; + arg->op.right = right; + + *tok = NULL; + type = process_arg(event, left, &token); + + again: + /* Handle other operations in the arguments */ + if (type == EVENT_OP && strcmp(token, ":") != 0) { + type = process_op(event, left, &token); + goto again; + } + + if (test_type_token(type, token, EVENT_OP, ":")) + goto out_free; + + arg->op.op = token; + + type = process_arg(event, right, &token); + + top->op.right = arg; + + *tok = token; + return type; + +out_free: + /* Top may point to itself */ + top->op.right = NULL; + free_token(token); + free_arg(arg); + return EVENT_ERROR; +} + +static enum event_type +process_array(struct event_format *event, struct print_arg *top, char **tok) +{ + struct print_arg *arg; + enum event_type type; + char *token = NULL; + + arg = alloc_arg(); + if (!arg) { + do_warning_event(event, "%s: not enough memory!", __func__); + /* '*tok' is set to top->op.op. No need to free. */ + *tok = NULL; + return EVENT_ERROR; + } + + *tok = NULL; + type = process_arg(event, arg, &token); + if (test_type_token(type, token, EVENT_OP, "]")) + goto out_free; + + top->op.right = arg; + + free_token(token); + type = read_token_item(&token); + *tok = token; + + return type; + +out_free: + free_token(token); + free_arg(arg); + return EVENT_ERROR; +} + +static int get_op_prio(char *op) +{ + if (!op[1]) { + switch (op[0]) { + case '~': + case '!': + return 4; + case '*': + case '/': + case '%': + return 6; + case '+': + case '-': + return 7; + /* '>>' and '<<' are 8 */ + case '<': + case '>': + return 9; + /* '==' and '!=' are 10 */ + case '&': + return 11; + case '^': + return 12; + case '|': + return 13; + case '?': + return 16; + default: + do_warning("unknown op '%c'", op[0]); + return -1; + } + } else { + if (strcmp(op, "++") == 0 || + strcmp(op, "--") == 0) { + return 3; + } else if (strcmp(op, ">>") == 0 || + strcmp(op, "<<") == 0) { + return 8; + } else if (strcmp(op, ">=") == 0 || + strcmp(op, "<=") == 0) { + return 9; + } else if (strcmp(op, "==") == 0 || + strcmp(op, "!=") == 0) { + return 10; + } else if (strcmp(op, "&&") == 0) { + return 14; + } else if (strcmp(op, "||") == 0) { + return 15; + } else { + do_warning("unknown op '%s'", op); + return -1; + } + } +} + +static int set_op_prio(struct print_arg *arg) +{ + + /* single ops are the greatest */ + if (!arg->op.left || arg->op.left->type == PRINT_NULL) + arg->op.prio = 0; + else + arg->op.prio = get_op_prio(arg->op.op); + + return arg->op.prio; +} + +/* Note, *tok does not get freed, but will most likely be saved */ +static enum event_type +process_op(struct event_format *event, struct print_arg *arg, char **tok) +{ + struct print_arg *left, *right = NULL; + enum event_type type; + char *token; + + /* the op is passed in via tok */ + token = *tok; + + if (arg->type == PRINT_OP && !arg->op.left) { + /* handle single op */ + if (token[1]) { + do_warning_event(event, "bad op token %s", token); + goto out_free; + } + switch (token[0]) { + case '~': + case '!': + case '+': + case '-': + break; + default: + do_warning_event(event, "bad op token %s", token); + goto out_free; + + } + + /* make an empty left */ + left = alloc_arg(); + if (!left) + goto out_warn_free; + + left->type = PRINT_NULL; + arg->op.left = left; + + right = alloc_arg(); + if (!right) + goto out_warn_free; + + arg->op.right = right; + + /* do not free the token, it belongs to an op */ + *tok = NULL; + type = process_arg(event, right, tok); + + } else if (strcmp(token, "?") == 0) { + + left = alloc_arg(); + if (!left) + goto out_warn_free; + + /* copy the top arg to the left */ + *left = *arg; + + arg->type = PRINT_OP; + arg->op.op = token; + arg->op.left = left; + arg->op.prio = 0; + + /* it will set arg->op.right */ + type = process_cond(event, arg, tok); + + } else if (strcmp(token, ">>") == 0 || + strcmp(token, "<<") == 0 || + strcmp(token, "&") == 0 || + strcmp(token, "|") == 0 || + strcmp(token, "&&") == 0 || + strcmp(token, "||") == 0 || + strcmp(token, "-") == 0 || + strcmp(token, "+") == 0 || + strcmp(token, "*") == 0 || + strcmp(token, "^") == 0 || + strcmp(token, "/") == 0 || + strcmp(token, "<") == 0 || + strcmp(token, ">") == 0 || + strcmp(token, "<=") == 0 || + strcmp(token, ">=") == 0 || + strcmp(token, "==") == 0 || + strcmp(token, "!=") == 0) { + + left = alloc_arg(); + if (!left) + goto out_warn_free; + + /* copy the top arg to the left */ + *left = *arg; + + arg->type = PRINT_OP; + arg->op.op = token; + arg->op.left = left; + arg->op.right = NULL; + + if (set_op_prio(arg) == -1) { + event->flags |= EVENT_FL_FAILED; + /* arg->op.op (= token) will be freed at out_free */ + arg->op.op = NULL; + goto out_free; + } + + type = read_token_item(&token); + *tok = token; + + /* could just be a type pointer */ + if ((strcmp(arg->op.op, "*") == 0) && + type == EVENT_DELIM && (strcmp(token, ")") == 0)) { + char *new_atom; + + if (left->type != PRINT_ATOM) { + do_warning_event(event, "bad pointer type"); + goto out_free; + } + new_atom = realloc(left->atom.atom, + strlen(left->atom.atom) + 3); + if (!new_atom) + goto out_warn_free; + + left->atom.atom = new_atom; + strcat(left->atom.atom, " *"); + free(arg->op.op); + *arg = *left; + free(left); + + return type; + } + + right = alloc_arg(); + if (!right) + goto out_warn_free; + + type = process_arg_token(event, right, tok, type); + + if (right->type == PRINT_OP && + get_op_prio(arg->op.op) < get_op_prio(right->op.op)) { + struct print_arg tmp; + + /* rotate ops according to the priority */ + arg->op.right = right->op.left; + + tmp = *arg; + *arg = *right; + *right = tmp; + + arg->op.left = right; + } else { + arg->op.right = right; + } + + } else if (strcmp(token, "[") == 0) { + + left = alloc_arg(); + if (!left) + goto out_warn_free; + + *left = *arg; + + arg->type = PRINT_OP; + arg->op.op = token; + arg->op.left = left; + + arg->op.prio = 0; + + /* it will set arg->op.right */ + type = process_array(event, arg, tok); + + } else { + do_warning_event(event, "unknown op '%s'", token); + event->flags |= EVENT_FL_FAILED; + /* the arg is now the left side */ + goto out_free; + } + + if (type == EVENT_OP && strcmp(*tok, ":") != 0) { + int prio; + + /* higher prios need to be closer to the root */ + prio = get_op_prio(*tok); + + if (prio > arg->op.prio) + return process_op(event, arg, tok); + + return process_op(event, right, tok); + } + + return type; + +out_warn_free: + do_warning_event(event, "%s: not enough memory!", __func__); +out_free: + free_token(token); + *tok = NULL; + return EVENT_ERROR; +} + +static enum event_type +process_entry(struct event_format *event __maybe_unused, struct print_arg *arg, + char **tok) +{ + enum event_type type; + char *field; + char *token; + + if (read_expected(EVENT_OP, "->") < 0) + goto out_err; + + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto out_free; + field = token; + + arg->type = PRINT_FIELD; + arg->field.name = field; + + if (is_flag_field) { + arg->field.field = pevent_find_any_field(event, arg->field.name); + arg->field.field->flags |= FIELD_IS_FLAG; + is_flag_field = 0; + } else if (is_symbolic_field) { + arg->field.field = pevent_find_any_field(event, arg->field.name); + arg->field.field->flags |= FIELD_IS_SYMBOLIC; + is_symbolic_field = 0; + } + + type = read_token(&token); + *tok = token; + + return type; + + out_free: + free_token(token); + out_err: + *tok = NULL; + return EVENT_ERROR; +} + +static int alloc_and_process_delim(struct event_format *event, char *next_token, + struct print_arg **print_arg) +{ + struct print_arg *field; + enum event_type type; + char *token; + int ret = 0; + + field = alloc_arg(); + if (!field) { + do_warning_event(event, "%s: not enough memory!", __func__); + errno = ENOMEM; + return -1; + } + + type = process_arg(event, field, &token); + + if (test_type_token(type, token, EVENT_DELIM, next_token)) { + errno = EINVAL; + ret = -1; + free_arg(field); + goto out_free_token; + } + + *print_arg = field; + +out_free_token: + free_token(token); + + return ret; +} + +static char *arg_eval (struct print_arg *arg); + +static unsigned long long +eval_type_str(unsigned long long val, const char *type, int pointer) +{ + int sign = 0; + char *ref; + int len; + + len = strlen(type); + + if (pointer) { + + if (type[len-1] != '*') { + do_warning("pointer expected with non pointer type"); + return val; + } + + ref = malloc(len); + if (!ref) { + do_warning("%s: not enough memory!", __func__); + return val; + } + memcpy(ref, type, len); + + /* chop off the " *" */ + ref[len - 2] = 0; + + val = eval_type_str(val, ref, 0); + free(ref); + return val; + } + + /* check if this is a pointer */ + if (type[len - 1] == '*') + return val; + + /* Try to figure out the arg size*/ + if (strncmp(type, "struct", 6) == 0) + /* all bets off */ + return val; + + if (strcmp(type, "u8") == 0) + return val & 0xff; + + if (strcmp(type, "u16") == 0) + return val & 0xffff; + + if (strcmp(type, "u32") == 0) + return val & 0xffffffff; + + if (strcmp(type, "u64") == 0 || + strcmp(type, "s64")) + return val; + + if (strcmp(type, "s8") == 0) + return (unsigned long long)(char)val & 0xff; + + if (strcmp(type, "s16") == 0) + return (unsigned long long)(short)val & 0xffff; + + if (strcmp(type, "s32") == 0) + return (unsigned long long)(int)val & 0xffffffff; + + if (strncmp(type, "unsigned ", 9) == 0) { + sign = 0; + type += 9; + } + + if (strcmp(type, "char") == 0) { + if (sign) + return (unsigned long long)(char)val & 0xff; + else + return val & 0xff; + } + + if (strcmp(type, "short") == 0) { + if (sign) + return (unsigned long long)(short)val & 0xffff; + else + return val & 0xffff; + } + + if (strcmp(type, "int") == 0) { + if (sign) + return (unsigned long long)(int)val & 0xffffffff; + else + return val & 0xffffffff; + } + + return val; +} + +/* + * Try to figure out the type. + */ +static unsigned long long +eval_type(unsigned long long val, struct print_arg *arg, int pointer) +{ + if (arg->type != PRINT_TYPE) { + do_warning("expected type argument"); + return 0; + } + + return eval_type_str(val, arg->typecast.type, pointer); +} + +static int arg_num_eval(struct print_arg *arg, long long *val) +{ + long long left, right; + int ret = 1; + + switch (arg->type) { + case PRINT_ATOM: + *val = strtoll(arg->atom.atom, NULL, 0); + break; + case PRINT_TYPE: + ret = arg_num_eval(arg->typecast.item, val); + if (!ret) + break; + *val = eval_type(*val, arg, 0); + break; + case PRINT_OP: + switch (arg->op.op[0]) { + case '|': + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; + if (arg->op.op[1]) + *val = left || right; + else + *val = left | right; + break; + case '&': + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; + if (arg->op.op[1]) + *val = left && right; + else + *val = left & right; + break; + case '<': + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; + switch (arg->op.op[1]) { + case 0: + *val = left < right; + break; + case '<': + *val = left << right; + break; + case '=': + *val = left <= right; + break; + default: + do_warning("unknown op '%s'", arg->op.op); + ret = 0; + } + break; + case '>': + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; + switch (arg->op.op[1]) { + case 0: + *val = left > right; + break; + case '>': + *val = left >> right; + break; + case '=': + *val = left >= right; + break; + default: + do_warning("unknown op '%s'", arg->op.op); + ret = 0; + } + break; + case '=': + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; + + if (arg->op.op[1] != '=') { + do_warning("unknown op '%s'", arg->op.op); + ret = 0; + } else + *val = left == right; + break; + case '!': + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; + + switch (arg->op.op[1]) { + case '=': + *val = left != right; + break; + default: + do_warning("unknown op '%s'", arg->op.op); + ret = 0; + } + break; + case '-': + /* check for negative */ + if (arg->op.left->type == PRINT_NULL) + left = 0; + else + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; + *val = left - right; + break; + case '+': + if (arg->op.left->type == PRINT_NULL) + left = 0; + else + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; + *val = left + right; + break; + default: + do_warning("unknown op '%s'", arg->op.op); + ret = 0; + } + break; + + case PRINT_NULL: + case PRINT_FIELD ... PRINT_SYMBOL: + case PRINT_STRING: + case PRINT_BSTRING: + case PRINT_BITMASK: + default: + do_warning("invalid eval type %d", arg->type); + ret = 0; + + } + return ret; +} + +static char *arg_eval (struct print_arg *arg) +{ + long long val; + static char buf[20]; + + switch (arg->type) { + case PRINT_ATOM: + return arg->atom.atom; + case PRINT_TYPE: + return arg_eval(arg->typecast.item); + case PRINT_OP: + if (!arg_num_eval(arg, &val)) + break; + sprintf(buf, "%lld", val); + return buf; + + case PRINT_NULL: + case PRINT_FIELD ... PRINT_SYMBOL: + case PRINT_STRING: + case PRINT_BSTRING: + case PRINT_BITMASK: + default: + do_warning("invalid eval type %d", arg->type); + break; + } + + return NULL; +} + +static enum event_type +process_fields(struct event_format *event, struct print_flag_sym **list, char **tok) +{ + enum event_type type; + struct print_arg *arg = NULL; + struct print_flag_sym *field; + char *token = *tok; + char *value; + + do { + free_token(token); + type = read_token_item(&token); + if (test_type_token(type, token, EVENT_OP, "{")) + break; + + arg = alloc_arg(); + if (!arg) + goto out_free; + + free_token(token); + type = process_arg(event, arg, &token); + + if (type == EVENT_OP) + type = process_op(event, arg, &token); + + if (type == EVENT_ERROR) + goto out_free; + + if (test_type_token(type, token, EVENT_DELIM, ",")) + goto out_free; + + field = calloc(1, sizeof(*field)); + if (!field) + goto out_free; + + value = arg_eval(arg); + if (value == NULL) + goto out_free_field; + field->value = strdup(value); + if (field->value == NULL) + goto out_free_field; + + free_arg(arg); + arg = alloc_arg(); + if (!arg) + goto out_free; + + free_token(token); + type = process_arg(event, arg, &token); + if (test_type_token(type, token, EVENT_OP, "}")) + goto out_free_field; + + value = arg_eval(arg); + if (value == NULL) + goto out_free_field; + field->str = strdup(value); + if (field->str == NULL) + goto out_free_field; + free_arg(arg); + arg = NULL; + + *list = field; + list = &field->next; + + free_token(token); + type = read_token_item(&token); + } while (type == EVENT_DELIM && strcmp(token, ",") == 0); + + *tok = token; + return type; + +out_free_field: + free_flag_sym(field); +out_free: + free_arg(arg); + free_token(token); + *tok = NULL; + + return EVENT_ERROR; +} + +static enum event_type +process_flags(struct event_format *event, struct print_arg *arg, char **tok) +{ + struct print_arg *field; + enum event_type type; + char *token = NULL; + + memset(arg, 0, sizeof(*arg)); + arg->type = PRINT_FLAGS; + + field = alloc_arg(); + if (!field) { + do_warning_event(event, "%s: not enough memory!", __func__); + goto out_free; + } + + type = process_field_arg(event, field, &token); + + /* Handle operations in the first argument */ + while (type == EVENT_OP) + type = process_op(event, field, &token); + + if (test_type_token(type, token, EVENT_DELIM, ",")) + goto out_free_field; + free_token(token); + + arg->flags.field = field; + + type = read_token_item(&token); + if (event_item_type(type)) { + arg->flags.delim = token; + type = read_token_item(&token); + } + + if (test_type_token(type, token, EVENT_DELIM, ",")) + goto out_free; + + type = process_fields(event, &arg->flags.flags, &token); + if (test_type_token(type, token, EVENT_DELIM, ")")) + goto out_free; + + free_token(token); + type = read_token_item(tok); + return type; + +out_free_field: + free_arg(field); +out_free: + free_token(token); + *tok = NULL; + return EVENT_ERROR; +} + +static enum event_type +process_symbols(struct event_format *event, struct print_arg *arg, char **tok) +{ + struct print_arg *field; + enum event_type type; + char *token = NULL; + + memset(arg, 0, sizeof(*arg)); + arg->type = PRINT_SYMBOL; + + field = alloc_arg(); + if (!field) { + do_warning_event(event, "%s: not enough memory!", __func__); + goto out_free; + } + + type = process_field_arg(event, field, &token); + + if (test_type_token(type, token, EVENT_DELIM, ",")) + goto out_free_field; + + arg->symbol.field = field; + + type = process_fields(event, &arg->symbol.symbols, &token); + if (test_type_token(type, token, EVENT_DELIM, ")")) + goto out_free; + + free_token(token); + type = read_token_item(tok); + return type; + +out_free_field: + free_arg(field); +out_free: + free_token(token); + *tok = NULL; + return EVENT_ERROR; +} + +static enum event_type +process_hex(struct event_format *event, struct print_arg *arg, char **tok) +{ + memset(arg, 0, sizeof(*arg)); + arg->type = PRINT_HEX; + + if (alloc_and_process_delim(event, ",", &arg->hex.field)) + goto out; + + if (alloc_and_process_delim(event, ")", &arg->hex.size)) + goto free_field; + + return read_token_item(tok); + +free_field: + free_arg(arg->hex.field); +out: + *tok = NULL; + return EVENT_ERROR; +} + +static enum event_type +process_int_array(struct event_format *event, struct print_arg *arg, char **tok) +{ + memset(arg, 0, sizeof(*arg)); + arg->type = PRINT_INT_ARRAY; + + if (alloc_and_process_delim(event, ",", &arg->int_array.field)) + goto out; + + if (alloc_and_process_delim(event, ",", &arg->int_array.count)) + goto free_field; + + if (alloc_and_process_delim(event, ")", &arg->int_array.el_size)) + goto free_size; + + return read_token_item(tok); + +free_size: + free_arg(arg->int_array.count); +free_field: + free_arg(arg->int_array.field); +out: + *tok = NULL; + return EVENT_ERROR; +} + +static enum event_type +process_dynamic_array(struct event_format *event, struct print_arg *arg, char **tok) +{ + struct format_field *field; + enum event_type type; + char *token; + + memset(arg, 0, sizeof(*arg)); + arg->type = PRINT_DYNAMIC_ARRAY; + + /* + * The item within the parenthesis is another field that holds + * the index into where the array starts. + */ + type = read_token(&token); + *tok = token; + if (type != EVENT_ITEM) + goto out_free; + + /* Find the field */ + + field = pevent_find_field(event, token); + if (!field) + goto out_free; + + arg->dynarray.field = field; + arg->dynarray.index = 0; + + if (read_expected(EVENT_DELIM, ")") < 0) + goto out_free; + + free_token(token); + type = read_token_item(&token); + *tok = token; + if (type != EVENT_OP || strcmp(token, "[") != 0) + return type; + + free_token(token); + arg = alloc_arg(); + if (!arg) { + do_warning_event(event, "%s: not enough memory!", __func__); + *tok = NULL; + return EVENT_ERROR; + } + + type = process_arg(event, arg, &token); + if (type == EVENT_ERROR) + goto out_free_arg; + + if (!test_type_token(type, token, EVENT_OP, "]")) + goto out_free_arg; + + free_token(token); + type = read_token_item(tok); + return type; + + out_free_arg: + free_arg(arg); + out_free: + free_token(token); + *tok = NULL; + return EVENT_ERROR; +} + +static enum event_type +process_paren(struct event_format *event, struct print_arg *arg, char **tok) +{ + struct print_arg *item_arg; + enum event_type type; + char *token; + + type = process_arg(event, arg, &token); + + if (type == EVENT_ERROR) + goto out_free; + + if (type == EVENT_OP) + type = process_op(event, arg, &token); + + if (type == EVENT_ERROR) + goto out_free; + + if (test_type_token(type, token, EVENT_DELIM, ")")) + goto out_free; + + free_token(token); + type = read_token_item(&token); + + /* + * If the next token is an item or another open paren, then + * this was a typecast. + */ + if (event_item_type(type) || + (type == EVENT_DELIM && strcmp(token, "(") == 0)) { + + /* make this a typecast and contine */ + + /* prevous must be an atom */ + if (arg->type != PRINT_ATOM) { + do_warning_event(event, "previous needed to be PRINT_ATOM"); + goto out_free; + } + + item_arg = alloc_arg(); + if (!item_arg) { + do_warning_event(event, "%s: not enough memory!", + __func__); + goto out_free; + } + + arg->type = PRINT_TYPE; + arg->typecast.type = arg->atom.atom; + arg->typecast.item = item_arg; + type = process_arg_token(event, item_arg, &token, type); + + } + + *tok = token; + return type; + + out_free: + free_token(token); + *tok = NULL; + return EVENT_ERROR; +} + + +static enum event_type +process_str(struct event_format *event __maybe_unused, struct print_arg *arg, + char **tok) +{ + enum event_type type; + char *token; + + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto out_free; + + arg->type = PRINT_STRING; + arg->string.string = token; + arg->string.offset = -1; + + if (read_expected(EVENT_DELIM, ")") < 0) + goto out_err; + + type = read_token(&token); + *tok = token; + + return type; + + out_free: + free_token(token); + out_err: + *tok = NULL; + return EVENT_ERROR; +} + +static enum event_type +process_bitmask(struct event_format *event __maybe_unused, struct print_arg *arg, + char **tok) +{ + enum event_type type; + char *token; + + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto out_free; + + arg->type = PRINT_BITMASK; + arg->bitmask.bitmask = token; + arg->bitmask.offset = -1; + + if (read_expected(EVENT_DELIM, ")") < 0) + goto out_err; + + type = read_token(&token); + *tok = token; + + return type; + + out_free: + free_token(token); + out_err: + *tok = NULL; + return EVENT_ERROR; +} + +static struct pevent_function_handler * +find_func_handler(struct pevent *pevent, char *func_name) +{ + struct pevent_function_handler *func; + + if (!pevent) + return NULL; + + for (func = pevent->func_handlers; func; func = func->next) { + if (strcmp(func->name, func_name) == 0) + break; + } + + return func; +} + +static void remove_func_handler(struct pevent *pevent, char *func_name) +{ + struct pevent_function_handler *func; + struct pevent_function_handler **next; + + next = &pevent->func_handlers; + while ((func = *next)) { + if (strcmp(func->name, func_name) == 0) { + *next = func->next; + free_func_handle(func); + break; + } + next = &func->next; + } +} + +static enum event_type +process_func_handler(struct event_format *event, struct pevent_function_handler *func, + struct print_arg *arg, char **tok) +{ + struct print_arg **next_arg; + struct print_arg *farg; + enum event_type type; + char *token; + int i; + + arg->type = PRINT_FUNC; + arg->func.func = func; + + *tok = NULL; + + next_arg = &(arg->func.args); + for (i = 0; i < func->nr_args; i++) { + farg = alloc_arg(); + if (!farg) { + do_warning_event(event, "%s: not enough memory!", + __func__); + return EVENT_ERROR; + } + + type = process_arg(event, farg, &token); + if (i < (func->nr_args - 1)) { + if (type != EVENT_DELIM || strcmp(token, ",") != 0) { + do_warning_event(event, + "Error: function '%s()' expects %d arguments but event %s only uses %d", + func->name, func->nr_args, + event->name, i + 1); + goto err; + } + } else { + if (type != EVENT_DELIM || strcmp(token, ")") != 0) { + do_warning_event(event, + "Error: function '%s()' only expects %d arguments but event %s has more", + func->name, func->nr_args, event->name); + goto err; + } + } + + *next_arg = farg; + next_arg = &(farg->next); + free_token(token); + } + + type = read_token(&token); + *tok = token; + + return type; + +err: + free_arg(farg); + free_token(token); + return EVENT_ERROR; +} + +static enum event_type +process_function(struct event_format *event, struct print_arg *arg, + char *token, char **tok) +{ + struct pevent_function_handler *func; + + if (strcmp(token, "__print_flags") == 0) { + free_token(token); + is_flag_field = 1; + return process_flags(event, arg, tok); + } + if (strcmp(token, "__print_symbolic") == 0) { + free_token(token); + is_symbolic_field = 1; + return process_symbols(event, arg, tok); + } + if (strcmp(token, "__print_hex") == 0) { + free_token(token); + return process_hex(event, arg, tok); + } + if (strcmp(token, "__print_array") == 0) { + free_token(token); + return process_int_array(event, arg, tok); + } + if (strcmp(token, "__get_str") == 0) { + free_token(token); + return process_str(event, arg, tok); + } + if (strcmp(token, "__get_bitmask") == 0) { + free_token(token); + return process_bitmask(event, arg, tok); + } + if (strcmp(token, "__get_dynamic_array") == 0) { + free_token(token); + return process_dynamic_array(event, arg, tok); + } + + func = find_func_handler(event->pevent, token); + if (func) { + free_token(token); + return process_func_handler(event, func, arg, tok); + } + + do_warning_event(event, "function %s not defined", token); + free_token(token); + return EVENT_ERROR; +} + +static enum event_type +process_arg_token(struct event_format *event, struct print_arg *arg, + char **tok, enum event_type type) +{ + char *token; + char *atom; + + token = *tok; + + switch (type) { + case EVENT_ITEM: + if (strcmp(token, "REC") == 0) { + free_token(token); + type = process_entry(event, arg, &token); + break; + } + atom = token; + /* test the next token */ + type = read_token_item(&token); + + /* + * If the next token is a parenthesis, then this + * is a function. + */ + if (type == EVENT_DELIM && strcmp(token, "(") == 0) { + free_token(token); + token = NULL; + /* this will free atom. */ + type = process_function(event, arg, atom, &token); + break; + } + /* atoms can be more than one token long */ + while (type == EVENT_ITEM) { + char *new_atom; + new_atom = realloc(atom, + strlen(atom) + strlen(token) + 2); + if (!new_atom) { + free(atom); + *tok = NULL; + free_token(token); + return EVENT_ERROR; + } + atom = new_atom; + strcat(atom, " "); + strcat(atom, token); + free_token(token); + type = read_token_item(&token); + } + + arg->type = PRINT_ATOM; + arg->atom.atom = atom; + break; + + case EVENT_DQUOTE: + case EVENT_SQUOTE: + arg->type = PRINT_ATOM; + arg->atom.atom = token; + type = read_token_item(&token); + break; + case EVENT_DELIM: + if (strcmp(token, "(") == 0) { + free_token(token); + type = process_paren(event, arg, &token); + break; + } + case EVENT_OP: + /* handle single ops */ + arg->type = PRINT_OP; + arg->op.op = token; + arg->op.left = NULL; + type = process_op(event, arg, &token); + + /* On error, the op is freed */ + if (type == EVENT_ERROR) + arg->op.op = NULL; + + /* return error type if errored */ + break; + + case EVENT_ERROR ... EVENT_NEWLINE: + default: + do_warning_event(event, "unexpected type %d", type); + return EVENT_ERROR; + } + *tok = token; + + return type; +} + +static int event_read_print_args(struct event_format *event, struct print_arg **list) +{ + enum event_type type = EVENT_ERROR; + struct print_arg *arg; + char *token; + int args = 0; + + do { + if (type == EVENT_NEWLINE) { + type = read_token_item(&token); + continue; + } + + arg = alloc_arg(); + if (!arg) { + do_warning_event(event, "%s: not enough memory!", + __func__); + return -1; + } + + type = process_arg(event, arg, &token); + + if (type == EVENT_ERROR) { + free_token(token); + free_arg(arg); + return -1; + } + + *list = arg; + args++; + + if (type == EVENT_OP) { + type = process_op(event, arg, &token); + free_token(token); + if (type == EVENT_ERROR) { + *list = NULL; + free_arg(arg); + return -1; + } + list = &arg->next; + continue; + } + + if (type == EVENT_DELIM && strcmp(token, ",") == 0) { + free_token(token); + *list = arg; + list = &arg->next; + continue; + } + break; + } while (type != EVENT_NONE); + + if (type != EVENT_NONE && type != EVENT_ERROR) + free_token(token); + + return args; +} + +static int event_read_print(struct event_format *event) +{ + enum event_type type; + char *token; + int ret; + + if (read_expected_item(EVENT_ITEM, "print") < 0) + return -1; + + if (read_expected(EVENT_ITEM, "fmt") < 0) + return -1; + + if (read_expected(EVENT_OP, ":") < 0) + return -1; + + if (read_expect_type(EVENT_DQUOTE, &token) < 0) + goto fail; + + concat: + event->print_fmt.format = token; + event->print_fmt.args = NULL; + + /* ok to have no arg */ + type = read_token_item(&token); + + if (type == EVENT_NONE) + return 0; + + /* Handle concatenation of print lines */ + if (type == EVENT_DQUOTE) { + char *cat; + + if (asprintf(&cat, "%s%s", event->print_fmt.format, token) < 0) + goto fail; + free_token(token); + free_token(event->print_fmt.format); + event->print_fmt.format = NULL; + token = cat; + goto concat; + } + + if (test_type_token(type, token, EVENT_DELIM, ",")) + goto fail; + + free_token(token); + + ret = event_read_print_args(event, &event->print_fmt.args); + if (ret < 0) + return -1; + + return ret; + + fail: + free_token(token); + return -1; +} + +/** + * pevent_find_common_field - return a common field by event + * @event: handle for the event + * @name: the name of the common field to return + * + * Returns a common field from the event by the given @name. + * This only searchs the common fields and not all field. + */ +struct format_field * +pevent_find_common_field(struct event_format *event, const char *name) +{ + struct format_field *format; + + for (format = event->format.common_fields; + format; format = format->next) { + if (strcmp(format->name, name) == 0) + break; + } + + return format; +} + +/** + * pevent_find_field - find a non-common field + * @event: handle for the event + * @name: the name of the non-common field + * + * Returns a non-common field by the given @name. + * This does not search common fields. + */ +struct format_field * +pevent_find_field(struct event_format *event, const char *name) +{ + struct format_field *format; + + for (format = event->format.fields; + format; format = format->next) { + if (strcmp(format->name, name) == 0) + break; + } + + return format; +} + +/** + * pevent_find_any_field - find any field by name + * @event: handle for the event + * @name: the name of the field + * + * Returns a field by the given @name. + * This searchs the common field names first, then + * the non-common ones if a common one was not found. + */ +struct format_field * +pevent_find_any_field(struct event_format *event, const char *name) +{ + struct format_field *format; + + format = pevent_find_common_field(event, name); + if (format) + return format; + return pevent_find_field(event, name); +} + +/** + * pevent_read_number - read a number from data + * @pevent: handle for the pevent + * @ptr: the raw data + * @size: the size of the data that holds the number + * + * Returns the number (converted to host) from the + * raw data. + */ +unsigned long long pevent_read_number(struct pevent *pevent, + const void *ptr, int size) +{ + switch (size) { + case 1: + return *(unsigned char *)ptr; + case 2: + return data2host2(pevent, ptr); + case 4: + return data2host4(pevent, ptr); + case 8: + return data2host8(pevent, ptr); + default: + /* BUG! */ + return 0; + } +} + +/** + * pevent_read_number_field - read a number from data + * @field: a handle to the field + * @data: the raw data to read + * @value: the value to place the number in + * + * Reads raw data according to a field offset and size, + * and translates it into @value. + * + * Returns 0 on success, -1 otherwise. + */ +int pevent_read_number_field(struct format_field *field, const void *data, + unsigned long long *value) +{ + if (!field) + return -1; + switch (field->size) { + case 1: + case 2: + case 4: + case 8: + *value = pevent_read_number(field->event->pevent, + data + field->offset, field->size); + return 0; + default: + return -1; + } +} + +static int get_common_info(struct pevent *pevent, + const char *type, int *offset, int *size) +{ + struct event_format *event; + struct format_field *field; + + /* + * All events should have the same common elements. + * Pick any event to find where the type is; + */ + if (!pevent->events) { + do_warning("no event_list!"); + return -1; + } + + event = pevent->events[0]; + field = pevent_find_common_field(event, type); + if (!field) + return -1; + + *offset = field->offset; + *size = field->size; + + return 0; +} + +static int __parse_common(struct pevent *pevent, void *data, + int *size, int *offset, const char *name) +{ + int ret; + + if (!*size) { + ret = get_common_info(pevent, name, offset, size); + if (ret < 0) + return ret; + } + return pevent_read_number(pevent, data + *offset, *size); +} + +static int trace_parse_common_type(struct pevent *pevent, void *data) +{ + return __parse_common(pevent, data, + &pevent->type_size, &pevent->type_offset, + "common_type"); +} + +static int parse_common_pid(struct pevent *pevent, void *data) +{ + return __parse_common(pevent, data, + &pevent->pid_size, &pevent->pid_offset, + "common_pid"); +} + +static int parse_common_pc(struct pevent *pevent, void *data) +{ + return __parse_common(pevent, data, + &pevent->pc_size, &pevent->pc_offset, + "common_preempt_count"); +} + +static int parse_common_flags(struct pevent *pevent, void *data) +{ + return __parse_common(pevent, data, + &pevent->flags_size, &pevent->flags_offset, + "common_flags"); +} + +static int parse_common_lock_depth(struct pevent *pevent, void *data) +{ + return __parse_common(pevent, data, + &pevent->ld_size, &pevent->ld_offset, + "common_lock_depth"); +} + +static int parse_common_migrate_disable(struct pevent *pevent, void *data) +{ + return __parse_common(pevent, data, + &pevent->ld_size, &pevent->ld_offset, + "common_migrate_disable"); +} + +static int events_id_cmp(const void *a, const void *b); + +/** + * pevent_find_event - find an event by given id + * @pevent: a handle to the pevent + * @id: the id of the event + * + * Returns an event that has a given @id. + */ +struct event_format *pevent_find_event(struct pevent *pevent, int id) +{ + struct event_format **eventptr; + struct event_format key; + struct event_format *pkey = &key; + + /* Check cache first */ + if (pevent->last_event && pevent->last_event->id == id) + return pevent->last_event; + + key.id = id; + + eventptr = bsearch(&pkey, pevent->events, pevent->nr_events, + sizeof(*pevent->events), events_id_cmp); + + if (eventptr) { + pevent->last_event = *eventptr; + return *eventptr; + } + + return NULL; +} + +/** + * pevent_find_event_by_name - find an event by given name + * @pevent: a handle to the pevent + * @sys: the system name to search for + * @name: the name of the event to search for + * + * This returns an event with a given @name and under the system + * @sys. If @sys is NULL the first event with @name is returned. + */ +struct event_format * +pevent_find_event_by_name(struct pevent *pevent, + const char *sys, const char *name) +{ + struct event_format *event; + int i; + + if (pevent->last_event && + strcmp(pevent->last_event->name, name) == 0 && + (!sys || strcmp(pevent->last_event->system, sys) == 0)) + return pevent->last_event; + + for (i = 0; i < pevent->nr_events; i++) { + event = pevent->events[i]; + if (strcmp(event->name, name) == 0) { + if (!sys) + break; + if (strcmp(event->system, sys) == 0) + break; + } + } + if (i == pevent->nr_events) + event = NULL; + + pevent->last_event = event; + return event; +} + +static unsigned long long +eval_num_arg(void *data, int size, struct event_format *event, struct print_arg *arg) +{ + struct pevent *pevent = event->pevent; + unsigned long long val = 0; + unsigned long long left, right; + struct print_arg *typearg = NULL; + struct print_arg *larg; + unsigned long offset; + unsigned int field_size; + + switch (arg->type) { + case PRINT_NULL: + /* ?? */ + return 0; + case PRINT_ATOM: + return strtoull(arg->atom.atom, NULL, 0); + case PRINT_FIELD: + if (!arg->field.field) { + arg->field.field = pevent_find_any_field(event, arg->field.name); + if (!arg->field.field) + goto out_warning_field; + + } + /* must be a number */ + val = pevent_read_number(pevent, data + arg->field.field->offset, + arg->field.field->size); + break; + case PRINT_FLAGS: + case PRINT_SYMBOL: + case PRINT_INT_ARRAY: + case PRINT_HEX: + break; + case PRINT_TYPE: + val = eval_num_arg(data, size, event, arg->typecast.item); + return eval_type(val, arg, 0); + case PRINT_STRING: + case PRINT_BSTRING: + case PRINT_BITMASK: + return 0; + case PRINT_FUNC: { + struct trace_seq s; + trace_seq_init(&s); + val = process_defined_func(&s, data, size, event, arg); + trace_seq_destroy(&s); + return val; + } + case PRINT_OP: + if (strcmp(arg->op.op, "[") == 0) { + /* + * Arrays are special, since we don't want + * to read the arg as is. + */ + right = eval_num_arg(data, size, event, arg->op.right); + + /* handle typecasts */ + larg = arg->op.left; + while (larg->type == PRINT_TYPE) { + if (!typearg) + typearg = larg; + larg = larg->typecast.item; + } + + /* Default to long size */ + field_size = pevent->long_size; + + switch (larg->type) { + case PRINT_DYNAMIC_ARRAY: + offset = pevent_read_number(pevent, + data + larg->dynarray.field->offset, + larg->dynarray.field->size); + if (larg->dynarray.field->elementsize) + field_size = larg->dynarray.field->elementsize; + /* + * The actual length of the dynamic array is stored + * in the top half of the field, and the offset + * is in the bottom half of the 32 bit field. + */ + offset &= 0xffff; + offset += right; + break; + case PRINT_FIELD: + if (!larg->field.field) { + larg->field.field = + pevent_find_any_field(event, larg->field.name); + if (!larg->field.field) { + arg = larg; + goto out_warning_field; + } + } + field_size = larg->field.field->elementsize; + offset = larg->field.field->offset + + right * larg->field.field->elementsize; + break; + default: + goto default_op; /* oops, all bets off */ + } + val = pevent_read_number(pevent, + data + offset, field_size); + if (typearg) + val = eval_type(val, typearg, 1); + break; + } else if (strcmp(arg->op.op, "?") == 0) { + left = eval_num_arg(data, size, event, arg->op.left); + arg = arg->op.right; + if (left) + val = eval_num_arg(data, size, event, arg->op.left); + else + val = eval_num_arg(data, size, event, arg->op.right); + break; + } + default_op: + left = eval_num_arg(data, size, event, arg->op.left); + right = eval_num_arg(data, size, event, arg->op.right); + switch (arg->op.op[0]) { + case '!': + switch (arg->op.op[1]) { + case 0: + val = !right; + break; + case '=': + val = left != right; + break; + default: + goto out_warning_op; + } + break; + case '~': + val = ~right; + break; + case '|': + if (arg->op.op[1]) + val = left || right; + else + val = left | right; + break; + case '&': + if (arg->op.op[1]) + val = left && right; + else + val = left & right; + break; + case '<': + switch (arg->op.op[1]) { + case 0: + val = left < right; + break; + case '<': + val = left << right; + break; + case '=': + val = left <= right; + break; + default: + goto out_warning_op; + } + break; + case '>': + switch (arg->op.op[1]) { + case 0: + val = left > right; + break; + case '>': + val = left >> right; + break; + case '=': + val = left >= right; + break; + default: + goto out_warning_op; + } + break; + case '=': + if (arg->op.op[1] != '=') + goto out_warning_op; + + val = left == right; + break; + case '-': + val = left - right; + break; + case '+': + val = left + right; + break; + case '/': + val = left / right; + break; + case '*': + val = left * right; + break; + default: + goto out_warning_op; + } + break; + case PRINT_DYNAMIC_ARRAY: + /* Without [], we pass the address to the dynamic data */ + offset = pevent_read_number(pevent, + data + arg->dynarray.field->offset, + arg->dynarray.field->size); + /* + * The actual length of the dynamic array is stored + * in the top half of the field, and the offset + * is in the bottom half of the 32 bit field. + */ + offset &= 0xffff; + val = (unsigned long long)((unsigned long)data + offset); + break; + default: /* not sure what to do there */ + return 0; + } + return val; + +out_warning_op: + do_warning_event(event, "%s: unknown op '%s'", __func__, arg->op.op); + return 0; + +out_warning_field: + do_warning_event(event, "%s: field %s not found", + __func__, arg->field.name); + return 0; +} + +struct flag { + const char *name; + unsigned long long value; +}; + +static const struct flag flags[] = { + { "HI_SOFTIRQ", 0 }, + { "TIMER_SOFTIRQ", 1 }, + { "NET_TX_SOFTIRQ", 2 }, + { "NET_RX_SOFTIRQ", 3 }, + { "BLOCK_SOFTIRQ", 4 }, + { "BLOCK_IOPOLL_SOFTIRQ", 5 }, + { "TASKLET_SOFTIRQ", 6 }, + { "SCHED_SOFTIRQ", 7 }, + { "HRTIMER_SOFTIRQ", 8 }, + { "RCU_SOFTIRQ", 9 }, + + { "HRTIMER_NORESTART", 0 }, + { "HRTIMER_RESTART", 1 }, +}; + +static long long eval_flag(const char *flag) +{ + int i; + + /* + * Some flags in the format files do not get converted. + * If the flag is not numeric, see if it is something that + * we already know about. + */ + if (isdigit(flag[0])) + return strtoull(flag, NULL, 0); + + for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++) + if (strcmp(flags[i].name, flag) == 0) + return flags[i].value; + + return -1LL; +} + +static void print_str_to_seq(struct trace_seq *s, const char *format, + int len_arg, const char *str) +{ + if (len_arg >= 0) + trace_seq_printf(s, format, len_arg, str); + else + trace_seq_printf(s, format, str); +} + +static void print_bitmask_to_seq(struct pevent *pevent, + struct trace_seq *s, const char *format, + int len_arg, const void *data, int size) +{ + int nr_bits = size * 8; + int str_size = (nr_bits + 3) / 4; + int len = 0; + char buf[3]; + char *str; + int index; + int i; + + /* + * The kernel likes to put in commas every 32 bits, we + * can do the same. + */ + str_size += (nr_bits - 1) / 32; + + str = malloc(str_size + 1); + if (!str) { + do_warning("%s: not enough memory!", __func__); + return; + } + str[str_size] = 0; + + /* Start out with -2 for the two chars per byte */ + for (i = str_size - 2; i >= 0; i -= 2) { + /* + * data points to a bit mask of size bytes. + * In the kernel, this is an array of long words, thus + * endianess is very important. + */ + if (pevent->file_bigendian) + index = size - (len + 1); + else + index = len; + + snprintf(buf, 3, "%02x", *((unsigned char *)data + index)); + memcpy(str + i, buf, 2); + len++; + if (!(len & 3) && i > 0) { + i--; + str[i] = ','; + } + } + + if (len_arg >= 0) + trace_seq_printf(s, format, len_arg, str); + else + trace_seq_printf(s, format, str); + + free(str); +} + +static void print_str_arg(struct trace_seq *s, void *data, int size, + struct event_format *event, const char *format, + int len_arg, struct print_arg *arg) +{ + struct pevent *pevent = event->pevent; + struct print_flag_sym *flag; + struct format_field *field; + struct printk_map *printk; + long long val, fval; + unsigned long addr; + char *str; + unsigned char *hex; + int print; + int i, len; + + switch (arg->type) { + case PRINT_NULL: + /* ?? */ + return; + case PRINT_ATOM: + print_str_to_seq(s, format, len_arg, arg->atom.atom); + return; + case PRINT_FIELD: + field = arg->field.field; + if (!field) { + field = pevent_find_any_field(event, arg->field.name); + if (!field) { + str = arg->field.name; + goto out_warning_field; + } + arg->field.field = field; + } + /* Zero sized fields, mean the rest of the data */ + len = field->size ? : size - field->offset; + + /* + * Some events pass in pointers. If this is not an array + * and the size is the same as long_size, assume that it + * is a pointer. + */ + if (!(field->flags & FIELD_IS_ARRAY) && + field->size == pevent->long_size) { + addr = *(unsigned long *)(data + field->offset); + /* Check if it matches a print format */ + printk = find_printk(pevent, addr); + if (printk) + trace_seq_puts(s, printk->printk); + else + trace_seq_printf(s, "%lx", addr); + break; + } + str = malloc(len + 1); + if (!str) { + do_warning_event(event, "%s: not enough memory!", + __func__); + return; + } + memcpy(str, data + field->offset, len); + str[len] = 0; + print_str_to_seq(s, format, len_arg, str); + free(str); + break; + case PRINT_FLAGS: + val = eval_num_arg(data, size, event, arg->flags.field); + print = 0; + for (flag = arg->flags.flags; flag; flag = flag->next) { + fval = eval_flag(flag->value); + if (!val && fval < 0) { + print_str_to_seq(s, format, len_arg, flag->str); + break; + } + if (fval > 0 && (val & fval) == fval) { + if (print && arg->flags.delim) + trace_seq_puts(s, arg->flags.delim); + print_str_to_seq(s, format, len_arg, flag->str); + print = 1; + val &= ~fval; + } + } + break; + case PRINT_SYMBOL: + val = eval_num_arg(data, size, event, arg->symbol.field); + for (flag = arg->symbol.symbols; flag; flag = flag->next) { + fval = eval_flag(flag->value); + if (val == fval) { + print_str_to_seq(s, format, len_arg, flag->str); + break; + } + } + break; + case PRINT_HEX: + if (arg->hex.field->type == PRINT_DYNAMIC_ARRAY) { + unsigned long offset; + offset = pevent_read_number(pevent, + data + arg->hex.field->dynarray.field->offset, + arg->hex.field->dynarray.field->size); + hex = data + (offset & 0xffff); + } else { + field = arg->hex.field->field.field; + if (!field) { + str = arg->hex.field->field.name; + field = pevent_find_any_field(event, str); + if (!field) + goto out_warning_field; + arg->hex.field->field.field = field; + } + hex = data + field->offset; + } + len = eval_num_arg(data, size, event, arg->hex.size); + for (i = 0; i < len; i++) { + if (i) + trace_seq_putc(s, ' '); + trace_seq_printf(s, "%02x", hex[i]); + } + break; + + case PRINT_INT_ARRAY: { + void *num; + int el_size; + + if (arg->int_array.field->type == PRINT_DYNAMIC_ARRAY) { + unsigned long offset; + struct format_field *field = + arg->int_array.field->dynarray.field; + offset = pevent_read_number(pevent, + data + field->offset, + field->size); + num = data + (offset & 0xffff); + } else { + field = arg->int_array.field->field.field; + if (!field) { + str = arg->int_array.field->field.name; + field = pevent_find_any_field(event, str); + if (!field) + goto out_warning_field; + arg->int_array.field->field.field = field; + } + num = data + field->offset; + } + len = eval_num_arg(data, size, event, arg->int_array.count); + el_size = eval_num_arg(data, size, event, + arg->int_array.el_size); + for (i = 0; i < len; i++) { + if (i) + trace_seq_putc(s, ' '); + + if (el_size == 1) { + trace_seq_printf(s, "%u", *(uint8_t *)num); + } else if (el_size == 2) { + trace_seq_printf(s, "%u", *(uint16_t *)num); + } else if (el_size == 4) { + trace_seq_printf(s, "%u", *(uint32_t *)num); + } else if (el_size == 8) { + trace_seq_printf(s, "%"PRIu64, *(uint64_t *)num); + } else { + trace_seq_printf(s, "BAD SIZE:%d 0x%x", + el_size, *(uint8_t *)num); + el_size = 1; + } + + num += el_size; + } + break; + } + case PRINT_TYPE: + break; + case PRINT_STRING: { + int str_offset; + + if (arg->string.offset == -1) { + struct format_field *f; + + f = pevent_find_any_field(event, arg->string.string); + arg->string.offset = f->offset; + } + str_offset = data2host4(pevent, data + arg->string.offset); + str_offset &= 0xffff; + print_str_to_seq(s, format, len_arg, ((char *)data) + str_offset); + break; + } + case PRINT_BSTRING: + print_str_to_seq(s, format, len_arg, arg->string.string); + break; + case PRINT_BITMASK: { + int bitmask_offset; + int bitmask_size; + + if (arg->bitmask.offset == -1) { + struct format_field *f; + + f = pevent_find_any_field(event, arg->bitmask.bitmask); + arg->bitmask.offset = f->offset; + } + bitmask_offset = data2host4(pevent, data + arg->bitmask.offset); + bitmask_size = bitmask_offset >> 16; + bitmask_offset &= 0xffff; + print_bitmask_to_seq(pevent, s, format, len_arg, + data + bitmask_offset, bitmask_size); + break; + } + case PRINT_OP: + /* + * The only op for string should be ? : + */ + if (arg->op.op[0] != '?') + return; + val = eval_num_arg(data, size, event, arg->op.left); + if (val) + print_str_arg(s, data, size, event, + format, len_arg, arg->op.right->op.left); + else + print_str_arg(s, data, size, event, + format, len_arg, arg->op.right->op.right); + break; + case PRINT_FUNC: + process_defined_func(s, data, size, event, arg); + break; + default: + /* well... */ + break; + } + + return; + +out_warning_field: + do_warning_event(event, "%s: field %s not found", + __func__, arg->field.name); +} + +static unsigned long long +process_defined_func(struct trace_seq *s, void *data, int size, + struct event_format *event, struct print_arg *arg) +{ + struct pevent_function_handler *func_handle = arg->func.func; + struct pevent_func_params *param; + unsigned long long *args; + unsigned long long ret; + struct print_arg *farg; + struct trace_seq str; + struct save_str { + struct save_str *next; + char *str; + } *strings = NULL, *string; + int i; + + if (!func_handle->nr_args) { + ret = (*func_handle->func)(s, NULL); + goto out; + } + + farg = arg->func.args; + param = func_handle->params; + + ret = ULLONG_MAX; + args = malloc(sizeof(*args) * func_handle->nr_args); + if (!args) + goto out; + + for (i = 0; i < func_handle->nr_args; i++) { + switch (param->type) { + case PEVENT_FUNC_ARG_INT: + case PEVENT_FUNC_ARG_LONG: + case PEVENT_FUNC_ARG_PTR: + args[i] = eval_num_arg(data, size, event, farg); + break; + case PEVENT_FUNC_ARG_STRING: + trace_seq_init(&str); + print_str_arg(&str, data, size, event, "%s", -1, farg); + trace_seq_terminate(&str); + string = malloc(sizeof(*string)); + if (!string) { + do_warning_event(event, "%s(%d): malloc str", + __func__, __LINE__); + goto out_free; + } + string->next = strings; + string->str = strdup(str.buffer); + if (!string->str) { + free(string); + do_warning_event(event, "%s(%d): malloc str", + __func__, __LINE__); + goto out_free; + } + args[i] = (uintptr_t)string->str; + strings = string; + trace_seq_destroy(&str); + break; + default: + /* + * Something went totally wrong, this is not + * an input error, something in this code broke. + */ + do_warning_event(event, "Unexpected end of arguments\n"); + goto out_free; + } + farg = farg->next; + param = param->next; + } + + ret = (*func_handle->func)(s, args); +out_free: + free(args); + while (strings) { + string = strings; + strings = string->next; + free(string->str); + free(string); + } + + out: + /* TBD : handle return type here */ + return ret; +} + +static void free_args(struct print_arg *args) +{ + struct print_arg *next; + + while (args) { + next = args->next; + + free_arg(args); + args = next; + } +} + +static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event_format *event) +{ + struct pevent *pevent = event->pevent; + struct format_field *field, *ip_field; + struct print_arg *args, *arg, **next; + unsigned long long ip, val; + char *ptr; + void *bptr; + int vsize; + + field = pevent->bprint_buf_field; + ip_field = pevent->bprint_ip_field; + + if (!field) { + field = pevent_find_field(event, "buf"); + if (!field) { + do_warning_event(event, "can't find buffer field for binary printk"); + return NULL; + } + ip_field = pevent_find_field(event, "ip"); + if (!ip_field) { + do_warning_event(event, "can't find ip field for binary printk"); + return NULL; + } + pevent->bprint_buf_field = field; + pevent->bprint_ip_field = ip_field; + } + + ip = pevent_read_number(pevent, data + ip_field->offset, ip_field->size); + + /* + * The first arg is the IP pointer. + */ + args = alloc_arg(); + if (!args) { + do_warning_event(event, "%s(%d): not enough memory!", + __func__, __LINE__); + return NULL; + } + arg = args; + arg->next = NULL; + next = &arg->next; + + arg->type = PRINT_ATOM; + + if (asprintf(&arg->atom.atom, "%lld", ip) < 0) + goto out_free; + + /* skip the first "%ps: " */ + for (ptr = fmt + 5, bptr = data + field->offset; + bptr < data + size && *ptr; ptr++) { + int ls = 0; + + if (*ptr == '%') { + process_again: + ptr++; + switch (*ptr) { + case '%': + break; + case 'l': + ls++; + goto process_again; + case 'L': + ls = 2; + goto process_again; + case '0' ... '9': + goto process_again; + case '.': + goto process_again; + case 'z': + case 'Z': + ls = 1; + goto process_again; + case 'p': + ls = 1; + /* fall through */ + case 'd': + case 'u': + case 'x': + case 'i': + switch (ls) { + case 0: + vsize = 4; + break; + case 1: + vsize = pevent->long_size; + break; + case 2: + vsize = 8; + break; + default: + vsize = ls; /* ? */ + break; + } + /* fall through */ + case '*': + if (*ptr == '*') + vsize = 4; + + /* the pointers are always 4 bytes aligned */ + bptr = (void *)(((unsigned long)bptr + 3) & + ~3); + val = pevent_read_number(pevent, bptr, vsize); + bptr += vsize; + arg = alloc_arg(); + if (!arg) { + do_warning_event(event, "%s(%d): not enough memory!", + __func__, __LINE__); + goto out_free; + } + arg->next = NULL; + arg->type = PRINT_ATOM; + if (asprintf(&arg->atom.atom, "%lld", val) < 0) { + free(arg); + goto out_free; + } + *next = arg; + next = &arg->next; + /* + * The '*' case means that an arg is used as the length. + * We need to continue to figure out for what. + */ + if (*ptr == '*') + goto process_again; + + break; + case 's': + arg = alloc_arg(); + if (!arg) { + do_warning_event(event, "%s(%d): not enough memory!", + __func__, __LINE__); + goto out_free; + } + arg->next = NULL; + arg->type = PRINT_BSTRING; + arg->string.string = strdup(bptr); + if (!arg->string.string) + goto out_free; + bptr += strlen(bptr) + 1; + *next = arg; + next = &arg->next; + default: + break; + } + } + } + + return args; + +out_free: + free_args(args); + return NULL; +} + +static char * +get_bprint_format(void *data, int size __maybe_unused, + struct event_format *event) +{ + struct pevent *pevent = event->pevent; + unsigned long long addr; + struct format_field *field; + struct printk_map *printk; + char *format; + + field = pevent->bprint_fmt_field; + + if (!field) { + field = pevent_find_field(event, "fmt"); + if (!field) { + do_warning_event(event, "can't find format field for binary printk"); + return NULL; + } + pevent->bprint_fmt_field = field; + } + + addr = pevent_read_number(pevent, data + field->offset, field->size); + + printk = find_printk(pevent, addr); + if (!printk) { + if (asprintf(&format, "%%pf: (NO FORMAT FOUND at %llx)\n", addr) < 0) + return NULL; + return format; + } + + if (asprintf(&format, "%s: %s", "%pf", printk->printk) < 0) + return NULL; + + return format; +} + +static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size, + struct event_format *event, struct print_arg *arg) +{ + unsigned char *buf; + const char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"; + + if (arg->type == PRINT_FUNC) { + process_defined_func(s, data, size, event, arg); + return; + } + + if (arg->type != PRINT_FIELD) { + trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", + arg->type); + return; + } + + if (mac == 'm') + fmt = "%.2x%.2x%.2x%.2x%.2x%.2x"; + if (!arg->field.field) { + arg->field.field = + pevent_find_any_field(event, arg->field.name); + if (!arg->field.field) { + do_warning_event(event, "%s: field %s not found", + __func__, arg->field.name); + return; + } + } + if (arg->field.field->size != 6) { + trace_seq_printf(s, "INVALIDMAC"); + return; + } + buf = data + arg->field.field->offset; + trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); +} + +static void print_ip4_addr(struct trace_seq *s, char i, unsigned char *buf) +{ + const char *fmt; + + if (i == 'i') + fmt = "%03d.%03d.%03d.%03d"; + else + fmt = "%d.%d.%d.%d"; + + trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3]); +} + +static inline bool ipv6_addr_v4mapped(const struct in6_addr *a) +{ + return ((unsigned long)(a->s6_addr32[0] | a->s6_addr32[1]) | + (unsigned long)(a->s6_addr32[2] ^ htonl(0x0000ffff))) == 0UL; +} + +static inline bool ipv6_addr_is_isatap(const struct in6_addr *addr) +{ + return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE); +} + +static void print_ip6c_addr(struct trace_seq *s, unsigned char *addr) +{ + int i, j, range; + unsigned char zerolength[8]; + int longest = 1; + int colonpos = -1; + uint16_t word; + uint8_t hi, lo; + bool needcolon = false; + bool useIPv4; + struct in6_addr in6; + + memcpy(&in6, addr, sizeof(struct in6_addr)); + + useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6); + + memset(zerolength, 0, sizeof(zerolength)); + + if (useIPv4) + range = 6; + else + range = 8; + + /* find position of longest 0 run */ + for (i = 0; i < range; i++) { + for (j = i; j < range; j++) { + if (in6.s6_addr16[j] != 0) + break; + zerolength[i]++; + } + } + for (i = 0; i < range; i++) { + if (zerolength[i] > longest) { + longest = zerolength[i]; + colonpos = i; + } + } + if (longest == 1) /* don't compress a single 0 */ + colonpos = -1; + + /* emit address */ + for (i = 0; i < range; i++) { + if (i == colonpos) { + if (needcolon || i == 0) + trace_seq_printf(s, ":"); + trace_seq_printf(s, ":"); + needcolon = false; + i += longest - 1; + continue; + } + if (needcolon) { + trace_seq_printf(s, ":"); + needcolon = false; + } + /* hex u16 without leading 0s */ + word = ntohs(in6.s6_addr16[i]); + hi = word >> 8; + lo = word & 0xff; + if (hi) + trace_seq_printf(s, "%x%02x", hi, lo); + else + trace_seq_printf(s, "%x", lo); + + needcolon = true; + } + + if (useIPv4) { + if (needcolon) + trace_seq_printf(s, ":"); + print_ip4_addr(s, 'I', &in6.s6_addr[12]); + } + + return; +} + +static void print_ip6_addr(struct trace_seq *s, char i, unsigned char *buf) +{ + int j; + + for (j = 0; j < 16; j += 2) { + trace_seq_printf(s, "%02x%02x", buf[j], buf[j+1]); + if (i == 'I' && j < 14) + trace_seq_printf(s, ":"); + } +} + +/* + * %pi4 print an IPv4 address with leading zeros + * %pI4 print an IPv4 address without leading zeros + * %pi6 print an IPv6 address without colons + * %pI6 print an IPv6 address with colons + * %pI6c print an IPv6 address in compressed form with colons + * %pISpc print an IP address based on sockaddr; p adds port. + */ +static int print_ipv4_arg(struct trace_seq *s, const char *ptr, char i, + void *data, int size, struct event_format *event, + struct print_arg *arg) +{ + unsigned char *buf; + + if (arg->type == PRINT_FUNC) { + process_defined_func(s, data, size, event, arg); + return 0; + } + + if (arg->type != PRINT_FIELD) { + trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type); + return 0; + } + + if (!arg->field.field) { + arg->field.field = + pevent_find_any_field(event, arg->field.name); + if (!arg->field.field) { + do_warning("%s: field %s not found", + __func__, arg->field.name); + return 0; + } + } + + buf = data + arg->field.field->offset; + + if (arg->field.field->size != 4) { + trace_seq_printf(s, "INVALIDIPv4"); + return 0; + } + print_ip4_addr(s, i, buf); + + return 0; +} + +static int print_ipv6_arg(struct trace_seq *s, const char *ptr, char i, + void *data, int size, struct event_format *event, + struct print_arg *arg) +{ + char have_c = 0; + unsigned char *buf; + int rc = 0; + + /* pI6c */ + if (i == 'I' && *ptr == 'c') { + have_c = 1; + ptr++; + rc++; + } + + if (arg->type == PRINT_FUNC) { + process_defined_func(s, data, size, event, arg); + return rc; + } + + if (arg->type != PRINT_FIELD) { + trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type); + return rc; + } + + if (!arg->field.field) { + arg->field.field = + pevent_find_any_field(event, arg->field.name); + if (!arg->field.field) { + do_warning("%s: field %s not found", + __func__, arg->field.name); + return rc; + } + } + + buf = data + arg->field.field->offset; + + if (arg->field.field->size != 16) { + trace_seq_printf(s, "INVALIDIPv6"); + return rc; + } + + if (have_c) + print_ip6c_addr(s, buf); + else + print_ip6_addr(s, i, buf); + + return rc; +} + +static int print_ipsa_arg(struct trace_seq *s, const char *ptr, char i, + void *data, int size, struct event_format *event, + struct print_arg *arg) +{ + char have_c = 0, have_p = 0; + unsigned char *buf; + struct sockaddr_storage *sa; + int rc = 0; + + /* pISpc */ + if (i == 'I') { + if (*ptr == 'p') { + have_p = 1; + ptr++; + rc++; + } + if (*ptr == 'c') { + have_c = 1; + ptr++; + rc++; + } + } + + if (arg->type == PRINT_FUNC) { + process_defined_func(s, data, size, event, arg); + return rc; + } + + if (arg->type != PRINT_FIELD) { + trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type); + return rc; + } + + if (!arg->field.field) { + arg->field.field = + pevent_find_any_field(event, arg->field.name); + if (!arg->field.field) { + do_warning("%s: field %s not found", + __func__, arg->field.name); + return rc; + } + } + + sa = (struct sockaddr_storage *) (data + arg->field.field->offset); + + if (sa->ss_family == AF_INET) { + struct sockaddr_in *sa4 = (struct sockaddr_in *) sa; + + if (arg->field.field->size < sizeof(struct sockaddr_in)) { + trace_seq_printf(s, "INVALIDIPv4"); + return rc; + } + + print_ip4_addr(s, i, (unsigned char *) &sa4->sin_addr); + if (have_p) + trace_seq_printf(s, ":%d", ntohs(sa4->sin_port)); + + + } else if (sa->ss_family == AF_INET6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa; + + if (arg->field.field->size < sizeof(struct sockaddr_in6)) { + trace_seq_printf(s, "INVALIDIPv6"); + return rc; + } + + if (have_p) + trace_seq_printf(s, "["); + + buf = (unsigned char *) &sa6->sin6_addr; + if (have_c) + print_ip6c_addr(s, buf); + else + print_ip6_addr(s, i, buf); + + if (have_p) + trace_seq_printf(s, "]:%d", ntohs(sa6->sin6_port)); + } + + return rc; +} + +static int print_ip_arg(struct trace_seq *s, const char *ptr, + void *data, int size, struct event_format *event, + struct print_arg *arg) +{ + char i = *ptr; /* 'i' or 'I' */ + char ver; + int rc = 0; + + ptr++; + rc++; + + ver = *ptr; + ptr++; + rc++; + + switch (ver) { + case '4': + rc += print_ipv4_arg(s, ptr, i, data, size, event, arg); + break; + case '6': + rc += print_ipv6_arg(s, ptr, i, data, size, event, arg); + break; + case 'S': + rc += print_ipsa_arg(s, ptr, i, data, size, event, arg); + break; + default: + return 0; + } + + return rc; +} + +static int is_printable_array(char *p, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len && p[i]; i++) + if (!isprint(p[i]) && !isspace(p[i])) + return 0; + return 1; +} + +static void print_event_fields(struct trace_seq *s, void *data, + int size __maybe_unused, + struct event_format *event) +{ + struct format_field *field; + unsigned long long val; + unsigned int offset, len, i; + + field = event->format.fields; + while (field) { + trace_seq_printf(s, " %s=", field->name); + if (field->flags & FIELD_IS_ARRAY) { + offset = field->offset; + len = field->size; + if (field->flags & FIELD_IS_DYNAMIC) { + val = pevent_read_number(event->pevent, data + offset, len); + offset = val; + len = offset >> 16; + offset &= 0xffff; + } + if (field->flags & FIELD_IS_STRING && + is_printable_array(data + offset, len)) { + trace_seq_printf(s, "%s", (char *)data + offset); + } else { + trace_seq_puts(s, "ARRAY["); + for (i = 0; i < len; i++) { + if (i) + trace_seq_puts(s, ", "); + trace_seq_printf(s, "%02x", + *((unsigned char *)data + offset + i)); + } + trace_seq_putc(s, ']'); + field->flags &= ~FIELD_IS_STRING; + } + } else { + val = pevent_read_number(event->pevent, data + field->offset, + field->size); + if (field->flags & FIELD_IS_POINTER) { + trace_seq_printf(s, "0x%llx", val); + } else if (field->flags & FIELD_IS_SIGNED) { + switch (field->size) { + case 4: + /* + * If field is long then print it in hex. + * A long usually stores pointers. + */ + if (field->flags & FIELD_IS_LONG) + trace_seq_printf(s, "0x%x", (int)val); + else + trace_seq_printf(s, "%d", (int)val); + break; + case 2: + trace_seq_printf(s, "%2d", (short)val); + break; + case 1: + trace_seq_printf(s, "%1d", (char)val); + break; + default: + trace_seq_printf(s, "%lld", val); + } + } else { + if (field->flags & FIELD_IS_LONG) + trace_seq_printf(s, "0x%llx", val); + else + trace_seq_printf(s, "%llu", val); + } + } + field = field->next; + } +} + +static void pretty_print(struct trace_seq *s, void *data, int size, struct event_format *event) +{ + struct pevent *pevent = event->pevent; + struct print_fmt *print_fmt = &event->print_fmt; + struct print_arg *arg = print_fmt->args; + struct print_arg *args = NULL; + const char *ptr = print_fmt->format; + unsigned long long val; + struct func_map *func; + const char *saveptr; + struct trace_seq p; + char *bprint_fmt = NULL; + char format[32]; + int show_func; + int len_as_arg; + int len_arg; + int len; + int ls; + + if (event->flags & EVENT_FL_FAILED) { + trace_seq_printf(s, "[FAILED TO PARSE]"); + print_event_fields(s, data, size, event); + return; + } + + if (event->flags & EVENT_FL_ISBPRINT) { + bprint_fmt = get_bprint_format(data, size, event); + args = make_bprint_args(bprint_fmt, data, size, event); + arg = args; + ptr = bprint_fmt; + } + + for (; *ptr; ptr++) { + ls = 0; + if (*ptr == '\\') { + ptr++; + switch (*ptr) { + case 'n': + trace_seq_putc(s, '\n'); + break; + case 't': + trace_seq_putc(s, '\t'); + break; + case 'r': + trace_seq_putc(s, '\r'); + break; + case '\\': + trace_seq_putc(s, '\\'); + break; + default: + trace_seq_putc(s, *ptr); + break; + } + + } else if (*ptr == '%') { + saveptr = ptr; + show_func = 0; + len_as_arg = 0; + cont_process: + ptr++; + switch (*ptr) { + case '%': + trace_seq_putc(s, '%'); + break; + case '#': + /* FIXME: need to handle properly */ + goto cont_process; + case 'h': + ls--; + goto cont_process; + case 'l': + ls++; + goto cont_process; + case 'L': + ls = 2; + goto cont_process; + case '*': + /* The argument is the length. */ + if (!arg) { + do_warning_event(event, "no argument match"); + event->flags |= EVENT_FL_FAILED; + goto out_failed; + } + len_arg = eval_num_arg(data, size, event, arg); + len_as_arg = 1; + arg = arg->next; + goto cont_process; + case '.': + case 'z': + case 'Z': + case '0' ... '9': + goto cont_process; + case 'p': + if (pevent->long_size == 4) + ls = 1; + else + ls = 2; + + if (*(ptr+1) == 'F' || + *(ptr+1) == 'f') { + ptr++; + show_func = *ptr; + } else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') { + print_mac_arg(s, *(ptr+1), data, size, event, arg); + ptr++; + arg = arg->next; + break; + } else if (*(ptr+1) == 'I' || *(ptr+1) == 'i') { + int n; + + n = print_ip_arg(s, ptr+1, data, size, event, arg); + if (n > 0) { + ptr += n; + arg = arg->next; + break; + } + } + + /* fall through */ + case 'd': + case 'i': + case 'x': + case 'X': + case 'u': + if (!arg) { + do_warning_event(event, "no argument match"); + event->flags |= EVENT_FL_FAILED; + goto out_failed; + } + + len = ((unsigned long)ptr + 1) - + (unsigned long)saveptr; + + /* should never happen */ + if (len > 31) { + do_warning_event(event, "bad format!"); + event->flags |= EVENT_FL_FAILED; + len = 31; + } + + memcpy(format, saveptr, len); + format[len] = 0; + + val = eval_num_arg(data, size, event, arg); + arg = arg->next; + + if (show_func) { + func = find_func(pevent, val); + if (func) { + trace_seq_puts(s, func->func); + if (show_func == 'F') + trace_seq_printf(s, + "+0x%llx", + val - func->addr); + break; + } + } + if (pevent->long_size == 8 && ls && + sizeof(long) != 8) { + char *p; + + ls = 2; + /* make %l into %ll */ + p = strchr(format, 'l'); + if (p) + memmove(p+1, p, strlen(p)+1); + else if (strcmp(format, "%p") == 0) + strcpy(format, "0x%llx"); + } + switch (ls) { + case -2: + if (len_as_arg) + trace_seq_printf(s, format, len_arg, (char)val); + else + trace_seq_printf(s, format, (char)val); + break; + case -1: + if (len_as_arg) + trace_seq_printf(s, format, len_arg, (short)val); + else + trace_seq_printf(s, format, (short)val); + break; + case 0: + if (len_as_arg) + trace_seq_printf(s, format, len_arg, (int)val); + else + trace_seq_printf(s, format, (int)val); + break; + case 1: + if (len_as_arg) + trace_seq_printf(s, format, len_arg, (long)val); + else + trace_seq_printf(s, format, (long)val); + break; + case 2: + if (len_as_arg) + trace_seq_printf(s, format, len_arg, + (long long)val); + else + trace_seq_printf(s, format, (long long)val); + break; + default: + do_warning_event(event, "bad count (%d)", ls); + event->flags |= EVENT_FL_FAILED; + } + break; + case 's': + if (!arg) { + do_warning_event(event, "no matching argument"); + event->flags |= EVENT_FL_FAILED; + goto out_failed; + } + + len = ((unsigned long)ptr + 1) - + (unsigned long)saveptr; + + /* should never happen */ + if (len > 31) { + do_warning_event(event, "bad format!"); + event->flags |= EVENT_FL_FAILED; + len = 31; + } + + memcpy(format, saveptr, len); + format[len] = 0; + if (!len_as_arg) + len_arg = -1; + /* Use helper trace_seq */ + trace_seq_init(&p); + print_str_arg(&p, data, size, event, + format, len_arg, arg); + trace_seq_terminate(&p); + trace_seq_puts(s, p.buffer); + trace_seq_destroy(&p); + arg = arg->next; + break; + default: + trace_seq_printf(s, ">%c<", *ptr); + + } + } else + trace_seq_putc(s, *ptr); + } + + if (event->flags & EVENT_FL_FAILED) { +out_failed: + trace_seq_printf(s, "[FAILED TO PARSE]"); + } + + if (args) { + free_args(args); + free(bprint_fmt); + } +} + +/** + * pevent_data_lat_fmt - parse the data for the latency format + * @pevent: a handle to the pevent + * @s: the trace_seq to write to + * @record: the record to read from + * + * This parses out the Latency format (interrupts disabled, + * need rescheduling, in hard/soft interrupt, preempt count + * and lock depth) and places it into the trace_seq. + */ +void pevent_data_lat_fmt(struct pevent *pevent, + struct trace_seq *s, struct pevent_record *record) +{ + static int check_lock_depth = 1; + static int check_migrate_disable = 1; + static int lock_depth_exists; + static int migrate_disable_exists; + unsigned int lat_flags; + unsigned int pc; + int lock_depth; + int migrate_disable; + int hardirq; + int softirq; + void *data = record->data; + + lat_flags = parse_common_flags(pevent, data); + pc = parse_common_pc(pevent, data); + /* lock_depth may not always exist */ + if (lock_depth_exists) + lock_depth = parse_common_lock_depth(pevent, data); + else if (check_lock_depth) { + lock_depth = parse_common_lock_depth(pevent, data); + if (lock_depth < 0) + check_lock_depth = 0; + else + lock_depth_exists = 1; + } + + /* migrate_disable may not always exist */ + if (migrate_disable_exists) + migrate_disable = parse_common_migrate_disable(pevent, data); + else if (check_migrate_disable) { + migrate_disable = parse_common_migrate_disable(pevent, data); + if (migrate_disable < 0) + check_migrate_disable = 0; + else + migrate_disable_exists = 1; + } + + hardirq = lat_flags & TRACE_FLAG_HARDIRQ; + softirq = lat_flags & TRACE_FLAG_SOFTIRQ; + + trace_seq_printf(s, "%c%c%c", + (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' : + (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ? + 'X' : '.', + (lat_flags & TRACE_FLAG_NEED_RESCHED) ? + 'N' : '.', + (hardirq && softirq) ? 'H' : + hardirq ? 'h' : softirq ? 's' : '.'); + + if (pc) + trace_seq_printf(s, "%x", pc); + else + trace_seq_putc(s, '.'); + + if (migrate_disable_exists) { + if (migrate_disable < 0) + trace_seq_putc(s, '.'); + else + trace_seq_printf(s, "%d", migrate_disable); + } + + if (lock_depth_exists) { + if (lock_depth < 0) + trace_seq_putc(s, '.'); + else + trace_seq_printf(s, "%d", lock_depth); + } + + trace_seq_terminate(s); +} + +/** + * pevent_data_type - parse out the given event type + * @pevent: a handle to the pevent + * @rec: the record to read from + * + * This returns the event id from the @rec. + */ +int pevent_data_type(struct pevent *pevent, struct pevent_record *rec) +{ + return trace_parse_common_type(pevent, rec->data); +} + +/** + * pevent_data_event_from_type - find the event by a given type + * @pevent: a handle to the pevent + * @type: the type of the event. + * + * This returns the event form a given @type; + */ +struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type) +{ + return pevent_find_event(pevent, type); +} + +/** + * pevent_data_pid - parse the PID from raw data + * @pevent: a handle to the pevent + * @rec: the record to parse + * + * This returns the PID from a raw data. + */ +int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec) +{ + return parse_common_pid(pevent, rec->data); +} + +/** + * pevent_data_comm_from_pid - return the command line from PID + * @pevent: a handle to the pevent + * @pid: the PID of the task to search for + * + * This returns a pointer to the command line that has the given + * @pid. + */ +const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid) +{ + const char *comm; + + comm = find_cmdline(pevent, pid); + return comm; +} + +static struct cmdline * +pid_from_cmdlist(struct pevent *pevent, const char *comm, struct cmdline *next) +{ + struct cmdline_list *cmdlist = (struct cmdline_list *)next; + + if (cmdlist) + cmdlist = cmdlist->next; + else + cmdlist = pevent->cmdlist; + + while (cmdlist && strcmp(cmdlist->comm, comm) != 0) + cmdlist = cmdlist->next; + + return (struct cmdline *)cmdlist; +} + +/** + * pevent_data_pid_from_comm - return the pid from a given comm + * @pevent: a handle to the pevent + * @comm: the cmdline to find the pid from + * @next: the cmdline structure to find the next comm + * + * This returns the cmdline structure that holds a pid for a given + * comm, or NULL if none found. As there may be more than one pid for + * a given comm, the result of this call can be passed back into + * a recurring call in the @next paramater, and then it will find the + * next pid. + * Also, it does a linear seach, so it may be slow. + */ +struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *comm, + struct cmdline *next) +{ + struct cmdline *cmdline; + + /* + * If the cmdlines have not been converted yet, then use + * the list. + */ + if (!pevent->cmdlines) + return pid_from_cmdlist(pevent, comm, next); + + if (next) { + /* + * The next pointer could have been still from + * a previous call before cmdlines were created + */ + if (next < pevent->cmdlines || + next >= pevent->cmdlines + pevent->cmdline_count) + next = NULL; + else + cmdline = next++; + } + + if (!next) + cmdline = pevent->cmdlines; + + while (cmdline < pevent->cmdlines + pevent->cmdline_count) { + if (strcmp(cmdline->comm, comm) == 0) + return cmdline; + cmdline++; + } + return NULL; +} + +/** + * pevent_cmdline_pid - return the pid associated to a given cmdline + * @cmdline: The cmdline structure to get the pid from + * + * Returns the pid for a give cmdline. If @cmdline is NULL, then + * -1 is returned. + */ +int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline) +{ + struct cmdline_list *cmdlist = (struct cmdline_list *)cmdline; + + if (!cmdline) + return -1; + + /* + * If cmdlines have not been created yet, or cmdline is + * not part of the array, then treat it as a cmdlist instead. + */ + if (!pevent->cmdlines || + cmdline < pevent->cmdlines || + cmdline >= pevent->cmdlines + pevent->cmdline_count) + return cmdlist->pid; + + return cmdline->pid; +} + +/** + * pevent_data_comm_from_pid - parse the data into the print format + * @s: the trace_seq to write to + * @event: the handle to the event + * @record: the record to read from + * + * This parses the raw @data using the given @event information and + * writes the print format into the trace_seq. + */ +void pevent_event_info(struct trace_seq *s, struct event_format *event, + struct pevent_record *record) +{ + int print_pretty = 1; + + if (event->pevent->print_raw || (event->flags & EVENT_FL_PRINTRAW)) + print_event_fields(s, record->data, record->size, event); + else { + + if (event->handler && !(event->flags & EVENT_FL_NOHANDLE)) + print_pretty = event->handler(s, record, event, + event->context); + + if (print_pretty) + pretty_print(s, record->data, record->size, event); + } + + trace_seq_terminate(s); +} + +static bool is_timestamp_in_us(char *trace_clock, bool use_trace_clock) +{ + if (!use_trace_clock) + return true; + + if (!strcmp(trace_clock, "local") || !strcmp(trace_clock, "global") + || !strcmp(trace_clock, "uptime") || !strcmp(trace_clock, "perf")) + return true; + + /* trace_clock is setting in tsc or counter mode */ + return false; +} + +void pevent_print_event(struct pevent *pevent, struct trace_seq *s, + struct pevent_record *record, bool use_trace_clock) +{ + static const char *spaces = " "; /* 20 spaces */ + struct event_format *event; + unsigned long secs; + unsigned long usecs; + unsigned long nsecs; + const char *comm; + void *data = record->data; + int type; + int pid; + int len; + int p; + bool use_usec_format; + + use_usec_format = is_timestamp_in_us(pevent->trace_clock, + use_trace_clock); + if (use_usec_format) { + secs = record->ts / NSECS_PER_SEC; + nsecs = record->ts - secs * NSECS_PER_SEC; + } + + if (record->size < 0) { + do_warning("ug! negative record size %d", record->size); + return; + } + + type = trace_parse_common_type(pevent, data); + + event = pevent_find_event(pevent, type); + if (!event) { + do_warning("ug! no event found for type %d", type); + return; + } + + pid = parse_common_pid(pevent, data); + comm = find_cmdline(pevent, pid); + + if (pevent->latency_format) { + trace_seq_printf(s, "%8.8s-%-5d %3d", + comm, pid, record->cpu); + pevent_data_lat_fmt(pevent, s, record); + } else + trace_seq_printf(s, "%16s-%-5d [%03d]", comm, pid, record->cpu); + + if (use_usec_format) { + if (pevent->flags & PEVENT_NSEC_OUTPUT) { + usecs = nsecs; + p = 9; + } else { + usecs = (nsecs + 500) / NSECS_PER_USEC; + p = 6; + } + + trace_seq_printf(s, " %5lu.%0*lu: %s: ", + secs, p, usecs, event->name); + } else + trace_seq_printf(s, " %12llu: %s: ", + record->ts, event->name); + + /* Space out the event names evenly. */ + len = strlen(event->name); + if (len < 20) + trace_seq_printf(s, "%.*s", 20 - len, spaces); + + pevent_event_info(s, event, record); +} + +static int events_id_cmp(const void *a, const void *b) +{ + struct event_format * const * ea = a; + struct event_format * const * eb = b; + + if ((*ea)->id < (*eb)->id) + return -1; + + if ((*ea)->id > (*eb)->id) + return 1; + + return 0; +} + +static int events_name_cmp(const void *a, const void *b) +{ + struct event_format * const * ea = a; + struct event_format * const * eb = b; + int res; + + res = strcmp((*ea)->name, (*eb)->name); + if (res) + return res; + + res = strcmp((*ea)->system, (*eb)->system); + if (res) + return res; + + return events_id_cmp(a, b); +} + +static int events_system_cmp(const void *a, const void *b) +{ + struct event_format * const * ea = a; + struct event_format * const * eb = b; + int res; + + res = strcmp((*ea)->system, (*eb)->system); + if (res) + return res; + + res = strcmp((*ea)->name, (*eb)->name); + if (res) + return res; + + return events_id_cmp(a, b); +} + +struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type sort_type) +{ + struct event_format **events; + int (*sort)(const void *a, const void *b); + + events = pevent->sort_events; + + if (events && pevent->last_type == sort_type) + return events; + + if (!events) { + events = malloc(sizeof(*events) * (pevent->nr_events + 1)); + if (!events) + return NULL; + + memcpy(events, pevent->events, sizeof(*events) * pevent->nr_events); + events[pevent->nr_events] = NULL; + + pevent->sort_events = events; + + /* the internal events are sorted by id */ + if (sort_type == EVENT_SORT_ID) { + pevent->last_type = sort_type; + return events; + } + } + + switch (sort_type) { + case EVENT_SORT_ID: + sort = events_id_cmp; + break; + case EVENT_SORT_NAME: + sort = events_name_cmp; + break; + case EVENT_SORT_SYSTEM: + sort = events_system_cmp; + break; + default: + return events; + } + + qsort(events, pevent->nr_events, sizeof(*events), sort); + pevent->last_type = sort_type; + + return events; +} + +static struct format_field ** +get_event_fields(const char *type, const char *name, + int count, struct format_field *list) +{ + struct format_field **fields; + struct format_field *field; + int i = 0; + + fields = malloc(sizeof(*fields) * (count + 1)); + if (!fields) + return NULL; + + for (field = list; field; field = field->next) { + fields[i++] = field; + if (i == count + 1) { + do_warning("event %s has more %s fields than specified", + name, type); + i--; + break; + } + } + + if (i != count) + do_warning("event %s has less %s fields than specified", + name, type); + + fields[i] = NULL; + + return fields; +} + +/** + * pevent_event_common_fields - return a list of common fields for an event + * @event: the event to return the common fields of. + * + * Returns an allocated array of fields. The last item in the array is NULL. + * The array must be freed with free(). + */ +struct format_field **pevent_event_common_fields(struct event_format *event) +{ + return get_event_fields("common", event->name, + event->format.nr_common, + event->format.common_fields); +} + +/** + * pevent_event_fields - return a list of event specific fields for an event + * @event: the event to return the fields of. + * + * Returns an allocated array of fields. The last item in the array is NULL. + * The array must be freed with free(). + */ +struct format_field **pevent_event_fields(struct event_format *event) +{ + return get_event_fields("event", event->name, + event->format.nr_fields, + event->format.fields); +} + +static void print_fields(struct trace_seq *s, struct print_flag_sym *field) +{ + trace_seq_printf(s, "{ %s, %s }", field->value, field->str); + if (field->next) { + trace_seq_puts(s, ", "); + print_fields(s, field->next); + } +} + +/* for debugging */ +static void print_args(struct print_arg *args) +{ + int print_paren = 1; + struct trace_seq s; + + switch (args->type) { + case PRINT_NULL: + printf("null"); + break; + case PRINT_ATOM: + printf("%s", args->atom.atom); + break; + case PRINT_FIELD: + printf("REC->%s", args->field.name); + break; + case PRINT_FLAGS: + printf("__print_flags("); + print_args(args->flags.field); + printf(", %s, ", args->flags.delim); + trace_seq_init(&s); + print_fields(&s, args->flags.flags); + trace_seq_do_printf(&s); + trace_seq_destroy(&s); + printf(")"); + break; + case PRINT_SYMBOL: + printf("__print_symbolic("); + print_args(args->symbol.field); + printf(", "); + trace_seq_init(&s); + print_fields(&s, args->symbol.symbols); + trace_seq_do_printf(&s); + trace_seq_destroy(&s); + printf(")"); + break; + case PRINT_HEX: + printf("__print_hex("); + print_args(args->hex.field); + printf(", "); + print_args(args->hex.size); + printf(")"); + break; + case PRINT_INT_ARRAY: + printf("__print_array("); + print_args(args->int_array.field); + printf(", "); + print_args(args->int_array.count); + printf(", "); + print_args(args->int_array.el_size); + printf(")"); + break; + case PRINT_STRING: + case PRINT_BSTRING: + printf("__get_str(%s)", args->string.string); + break; + case PRINT_BITMASK: + printf("__get_bitmask(%s)", args->bitmask.bitmask); + break; + case PRINT_TYPE: + printf("(%s)", args->typecast.type); + print_args(args->typecast.item); + break; + case PRINT_OP: + if (strcmp(args->op.op, ":") == 0) + print_paren = 0; + if (print_paren) + printf("("); + print_args(args->op.left); + printf(" %s ", args->op.op); + print_args(args->op.right); + if (print_paren) + printf(")"); + break; + default: + /* we should warn... */ + return; + } + if (args->next) { + printf("\n"); + print_args(args->next); + } +} + +static void parse_header_field(const char *field, + int *offset, int *size, int mandatory) +{ + unsigned long long save_input_buf_ptr; + unsigned long long save_input_buf_siz; + char *token; + int type; + + save_input_buf_ptr = input_buf_ptr; + save_input_buf_siz = input_buf_siz; + + if (read_expected(EVENT_ITEM, "field") < 0) + return; + if (read_expected(EVENT_OP, ":") < 0) + return; + + /* type */ + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + free_token(token); + + /* + * If this is not a mandatory field, then test it first. + */ + if (mandatory) { + if (read_expected(EVENT_ITEM, field) < 0) + return; + } else { + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + if (strcmp(token, field) != 0) + goto discard; + free_token(token); + } + + if (read_expected(EVENT_OP, ";") < 0) + return; + if (read_expected(EVENT_ITEM, "offset") < 0) + return; + if (read_expected(EVENT_OP, ":") < 0) + return; + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + *offset = atoi(token); + free_token(token); + if (read_expected(EVENT_OP, ";") < 0) + return; + if (read_expected(EVENT_ITEM, "size") < 0) + return; + if (read_expected(EVENT_OP, ":") < 0) + return; + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + *size = atoi(token); + free_token(token); + if (read_expected(EVENT_OP, ";") < 0) + return; + type = read_token(&token); + if (type != EVENT_NEWLINE) { + /* newer versions of the kernel have a "signed" type */ + if (type != EVENT_ITEM) + goto fail; + + if (strcmp(token, "signed") != 0) + goto fail; + + free_token(token); + + if (read_expected(EVENT_OP, ":") < 0) + return; + + if (read_expect_type(EVENT_ITEM, &token)) + goto fail; + + free_token(token); + if (read_expected(EVENT_OP, ";") < 0) + return; + + if (read_expect_type(EVENT_NEWLINE, &token)) + goto fail; + } + fail: + free_token(token); + return; + + discard: + input_buf_ptr = save_input_buf_ptr; + input_buf_siz = save_input_buf_siz; + *offset = 0; + *size = 0; + free_token(token); +} + +/** + * pevent_parse_header_page - parse the data stored in the header page + * @pevent: the handle to the pevent + * @buf: the buffer storing the header page format string + * @size: the size of @buf + * @long_size: the long size to use if there is no header + * + * This parses the header page format for information on the + * ring buffer used. The @buf should be copied from + * + * /sys/kernel/debug/tracing/events/header_page + */ +int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, + int long_size) +{ + int ignore; + + if (!size) { + /* + * Old kernels did not have header page info. + * Sorry but we just use what we find here in user space. + */ + pevent->header_page_ts_size = sizeof(long long); + pevent->header_page_size_size = long_size; + pevent->header_page_data_offset = sizeof(long long) + long_size; + pevent->old_format = 1; + return -1; + } + init_input_buf(buf, size); + + parse_header_field("timestamp", &pevent->header_page_ts_offset, + &pevent->header_page_ts_size, 1); + parse_header_field("commit", &pevent->header_page_size_offset, + &pevent->header_page_size_size, 1); + parse_header_field("overwrite", &pevent->header_page_overwrite, + &ignore, 0); + parse_header_field("data", &pevent->header_page_data_offset, + &pevent->header_page_data_size, 1); + + return 0; +} + +static int event_matches(struct event_format *event, + int id, const char *sys_name, + const char *event_name) +{ + if (id >= 0 && id != event->id) + return 0; + + if (event_name && (strcmp(event_name, event->name) != 0)) + return 0; + + if (sys_name && (strcmp(sys_name, event->system) != 0)) + return 0; + + return 1; +} + +static void free_handler(struct event_handler *handle) +{ + free((void *)handle->sys_name); + free((void *)handle->event_name); + free(handle); +} + +static int find_event_handle(struct pevent *pevent, struct event_format *event) +{ + struct event_handler *handle, **next; + + for (next = &pevent->handlers; *next; + next = &(*next)->next) { + handle = *next; + if (event_matches(event, handle->id, + handle->sys_name, + handle->event_name)) + break; + } + + if (!(*next)) + return 0; + + pr_stat("overriding event (%d) %s:%s with new print handler", + event->id, event->system, event->name); + + event->handler = handle->func; + event->context = handle->context; + + *next = handle->next; + free_handler(handle); + + return 1; +} + +/** + * __pevent_parse_format - parse the event format + * @buf: the buffer storing the event format string + * @size: the size of @buf + * @sys: the system the event belongs to + * + * This parses the event format and creates an event structure + * to quickly parse raw data for a given event. + * + * These files currently come from: + * + * /sys/kernel/debug/tracing/events/.../.../format + */ +enum pevent_errno __pevent_parse_format(struct event_format **eventp, + struct pevent *pevent, const char *buf, + unsigned long size, const char *sys) +{ + struct event_format *event; + int ret; + + init_input_buf(buf, size); + + *eventp = event = alloc_event(); + if (!event) + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + + event->name = event_read_name(); + if (!event->name) { + /* Bad event? */ + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; + goto event_alloc_failed; + } + + if (strcmp(sys, "ftrace") == 0) { + event->flags |= EVENT_FL_ISFTRACE; + + if (strcmp(event->name, "bprint") == 0) + event->flags |= EVENT_FL_ISBPRINT; + } + + event->id = event_read_id(); + if (event->id < 0) { + ret = PEVENT_ERRNO__READ_ID_FAILED; + /* + * This isn't an allocation error actually. + * But as the ID is critical, just bail out. + */ + goto event_alloc_failed; + } + + event->system = strdup(sys); + if (!event->system) { + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; + goto event_alloc_failed; + } + + /* Add pevent to event so that it can be referenced */ + event->pevent = pevent; + + ret = event_read_format(event); + if (ret < 0) { + ret = PEVENT_ERRNO__READ_FORMAT_FAILED; + goto event_parse_failed; + } + + /* + * If the event has an override, don't print warnings if the event + * print format fails to parse. + */ + if (pevent && find_event_handle(pevent, event)) + show_warning = 0; + + ret = event_read_print(event); + show_warning = 1; + + if (ret < 0) { + ret = PEVENT_ERRNO__READ_PRINT_FAILED; + goto event_parse_failed; + } + + if (!ret && (event->flags & EVENT_FL_ISFTRACE)) { + struct format_field *field; + struct print_arg *arg, **list; + + /* old ftrace had no args */ + list = &event->print_fmt.args; + for (field = event->format.fields; field; field = field->next) { + arg = alloc_arg(); + if (!arg) { + event->flags |= EVENT_FL_FAILED; + return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED; + } + arg->type = PRINT_FIELD; + arg->field.name = strdup(field->name); + if (!arg->field.name) { + event->flags |= EVENT_FL_FAILED; + free_arg(arg); + return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED; + } + arg->field.field = field; + *list = arg; + list = &arg->next; + } + return 0; + } + + return 0; + + event_parse_failed: + event->flags |= EVENT_FL_FAILED; + return ret; + + event_alloc_failed: + free(event->system); + free(event->name); + free(event); + *eventp = NULL; + return ret; +} + +static enum pevent_errno +__pevent_parse_event(struct pevent *pevent, + struct event_format **eventp, + const char *buf, unsigned long size, + const char *sys) +{ + int ret = __pevent_parse_format(eventp, pevent, buf, size, sys); + struct event_format *event = *eventp; + + if (event == NULL) + return ret; + + if (pevent && add_event(pevent, event)) { + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; + goto event_add_failed; + } + +#define PRINT_ARGS 0 + if (PRINT_ARGS && event->print_fmt.args) + print_args(event->print_fmt.args); + + return 0; + +event_add_failed: + pevent_free_format(event); + return ret; +} + +/** + * pevent_parse_format - parse the event format + * @pevent: the handle to the pevent + * @eventp: returned format + * @buf: the buffer storing the event format string + * @size: the size of @buf + * @sys: the system the event belongs to + * + * This parses the event format and creates an event structure + * to quickly parse raw data for a given event. + * + * These files currently come from: + * + * /sys/kernel/debug/tracing/events/.../.../format + */ +enum pevent_errno pevent_parse_format(struct pevent *pevent, + struct event_format **eventp, + const char *buf, + unsigned long size, const char *sys) +{ + return __pevent_parse_event(pevent, eventp, buf, size, sys); +} + +/** + * pevent_parse_event - parse the event format + * @pevent: the handle to the pevent + * @buf: the buffer storing the event format string + * @size: the size of @buf + * @sys: the system the event belongs to + * + * This parses the event format and creates an event structure + * to quickly parse raw data for a given event. + * + * These files currently come from: + * + * /sys/kernel/debug/tracing/events/.../.../format + */ +enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, + unsigned long size, const char *sys) +{ + struct event_format *event = NULL; + return __pevent_parse_event(pevent, &event, buf, size, sys); +} + +#undef _PE +#define _PE(code, str) str +static const char * const pevent_error_str[] = { + PEVENT_ERRORS +}; +#undef _PE + +int pevent_strerror(struct pevent *pevent __maybe_unused, + enum pevent_errno errnum, char *buf, size_t buflen) +{ + int idx; + const char *msg; + + if (errnum >= 0) { + msg = strerror_r(errnum, buf, buflen); + if (msg != buf) { + size_t len = strlen(msg); + memcpy(buf, msg, min(buflen - 1, len)); + *(buf + min(buflen - 1, len)) = '\0'; + } + return 0; + } + + if (errnum <= __PEVENT_ERRNO__START || + errnum >= __PEVENT_ERRNO__END) + return -1; + + idx = errnum - __PEVENT_ERRNO__START - 1; + msg = pevent_error_str[idx]; + snprintf(buf, buflen, "%s", msg); + + return 0; +} + +int get_field_val(struct trace_seq *s, struct format_field *field, + const char *name, struct pevent_record *record, + unsigned long long *val, int err) +{ + if (!field) { + if (err) + trace_seq_printf(s, "", name); + return -1; + } + + if (pevent_read_number_field(field, record->data, val)) { + if (err) + trace_seq_printf(s, " %s=INVALID", name); + return -1; + } + + return 0; +} + +/** + * pevent_get_field_raw - return the raw pointer into the data field + * @s: The seq to print to on error + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @len: place to store the field length. + * @err: print default error if failed. + * + * Returns a pointer into record->data of the field and places + * the length of the field in @len. + * + * On failure, it returns NULL. + */ +void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, + const char *name, struct pevent_record *record, + int *len, int err) +{ + struct format_field *field; + void *data = record->data; + unsigned offset; + int dummy; + + if (!event) + return NULL; + + field = pevent_find_field(event, name); + + if (!field) { + if (err) + trace_seq_printf(s, "", name); + return NULL; + } + + /* Allow @len to be NULL */ + if (!len) + len = &dummy; + + offset = field->offset; + if (field->flags & FIELD_IS_DYNAMIC) { + offset = pevent_read_number(event->pevent, + data + offset, field->size); + *len = offset >> 16; + offset &= 0xffff; + } else + *len = field->size; + + return data + offset; +} + +/** + * pevent_get_field_val - find a field and return its value + * @s: The seq to print to on error + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @val: place to store the value of the field. + * @err: print default error if failed. + * + * Returns 0 on success -1 on field not found. + */ +int pevent_get_field_val(struct trace_seq *s, struct event_format *event, + const char *name, struct pevent_record *record, + unsigned long long *val, int err) +{ + struct format_field *field; + + if (!event) + return -1; + + field = pevent_find_field(event, name); + + return get_field_val(s, field, name, record, val, err); +} + +/** + * pevent_get_common_field_val - find a common field and return its value + * @s: The seq to print to on error + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @val: place to store the value of the field. + * @err: print default error if failed. + * + * Returns 0 on success -1 on field not found. + */ +int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event, + const char *name, struct pevent_record *record, + unsigned long long *val, int err) +{ + struct format_field *field; + + if (!event) + return -1; + + field = pevent_find_common_field(event, name); + + return get_field_val(s, field, name, record, val, err); +} + +/** + * pevent_get_any_field_val - find a any field and return its value + * @s: The seq to print to on error + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @val: place to store the value of the field. + * @err: print default error if failed. + * + * Returns 0 on success -1 on field not found. + */ +int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event, + const char *name, struct pevent_record *record, + unsigned long long *val, int err) +{ + struct format_field *field; + + if (!event) + return -1; + + field = pevent_find_any_field(event, name); + + return get_field_val(s, field, name, record, val, err); +} + +/** + * pevent_print_num_field - print a field and a format + * @s: The seq to print to + * @fmt: The printf format to print the field with. + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @err: print default error if failed. + * + * Returns: 0 on success, -1 field not found, or 1 if buffer is full. + */ +int pevent_print_num_field(struct trace_seq *s, const char *fmt, + struct event_format *event, const char *name, + struct pevent_record *record, int err) +{ + struct format_field *field = pevent_find_field(event, name); + unsigned long long val; + + if (!field) + goto failed; + + if (pevent_read_number_field(field, record->data, &val)) + goto failed; + + return trace_seq_printf(s, fmt, val); + + failed: + if (err) + trace_seq_printf(s, "CAN'T FIND FIELD \"%s\"", name); + return -1; +} + +/** + * pevent_print_func_field - print a field and a format for function pointers + * @s: The seq to print to + * @fmt: The printf format to print the field with. + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @err: print default error if failed. + * + * Returns: 0 on success, -1 field not found, or 1 if buffer is full. + */ +int pevent_print_func_field(struct trace_seq *s, const char *fmt, + struct event_format *event, const char *name, + struct pevent_record *record, int err) +{ + struct format_field *field = pevent_find_field(event, name); + struct pevent *pevent = event->pevent; + unsigned long long val; + struct func_map *func; + char tmp[128]; + + if (!field) + goto failed; + + if (pevent_read_number_field(field, record->data, &val)) + goto failed; + + func = find_func(pevent, val); + + if (func) + snprintf(tmp, 128, "%s/0x%llx", func->func, func->addr - val); + else + sprintf(tmp, "0x%08llx", val); + + return trace_seq_printf(s, fmt, tmp); + + failed: + if (err) + trace_seq_printf(s, "CAN'T FIND FIELD \"%s\"", name); + return -1; +} + +static void free_func_handle(struct pevent_function_handler *func) +{ + struct pevent_func_params *params; + + free(func->name); + + while (func->params) { + params = func->params; + func->params = params->next; + free(params); + } + + free(func); +} + +/** + * pevent_register_print_function - register a helper function + * @pevent: the handle to the pevent + * @func: the function to process the helper function + * @ret_type: the return type of the helper function + * @name: the name of the helper function + * @parameters: A list of enum pevent_func_arg_type + * + * Some events may have helper functions in the print format arguments. + * This allows a plugin to dynamically create a way to process one + * of these functions. + * + * The @parameters is a variable list of pevent_func_arg_type enums that + * must end with PEVENT_FUNC_ARG_VOID. + */ +int pevent_register_print_function(struct pevent *pevent, + pevent_func_handler func, + enum pevent_func_arg_type ret_type, + char *name, ...) +{ + struct pevent_function_handler *func_handle; + struct pevent_func_params **next_param; + struct pevent_func_params *param; + enum pevent_func_arg_type type; + va_list ap; + int ret; + + func_handle = find_func_handler(pevent, name); + if (func_handle) { + /* + * This is most like caused by the users own + * plugins updating the function. This overrides the + * system defaults. + */ + pr_stat("override of function helper '%s'", name); + remove_func_handler(pevent, name); + } + + func_handle = calloc(1, sizeof(*func_handle)); + if (!func_handle) { + do_warning("Failed to allocate function handler"); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } + + func_handle->ret_type = ret_type; + func_handle->name = strdup(name); + func_handle->func = func; + if (!func_handle->name) { + do_warning("Failed to allocate function name"); + free(func_handle); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } + + next_param = &(func_handle->params); + va_start(ap, name); + for (;;) { + type = va_arg(ap, enum pevent_func_arg_type); + if (type == PEVENT_FUNC_ARG_VOID) + break; + + if (type >= PEVENT_FUNC_ARG_MAX_TYPES) { + do_warning("Invalid argument type %d", type); + ret = PEVENT_ERRNO__INVALID_ARG_TYPE; + goto out_free; + } + + param = malloc(sizeof(*param)); + if (!param) { + do_warning("Failed to allocate function param"); + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; + goto out_free; + } + param->type = type; + param->next = NULL; + + *next_param = param; + next_param = &(param->next); + + func_handle->nr_args++; + } + va_end(ap); + + func_handle->next = pevent->func_handlers; + pevent->func_handlers = func_handle; + + return 0; + out_free: + va_end(ap); + free_func_handle(func_handle); + return ret; +} + +/** + * pevent_unregister_print_function - unregister a helper function + * @pevent: the handle to the pevent + * @func: the function to process the helper function + * @name: the name of the helper function + * + * This function removes existing print handler for function @name. + * + * Returns 0 if the handler was removed successully, -1 otherwise. + */ +int pevent_unregister_print_function(struct pevent *pevent, + pevent_func_handler func, char *name) +{ + struct pevent_function_handler *func_handle; + + func_handle = find_func_handler(pevent, name); + if (func_handle && func_handle->func == func) { + remove_func_handler(pevent, name); + return 0; + } + return -1; +} + +static struct event_format *pevent_search_event(struct pevent *pevent, int id, + const char *sys_name, + const char *event_name) +{ + struct event_format *event; + + if (id >= 0) { + /* search by id */ + event = pevent_find_event(pevent, id); + if (!event) + return NULL; + if (event_name && (strcmp(event_name, event->name) != 0)) + return NULL; + if (sys_name && (strcmp(sys_name, event->system) != 0)) + return NULL; + } else { + event = pevent_find_event_by_name(pevent, sys_name, event_name); + if (!event) + return NULL; + } + return event; +} + +/** + * pevent_register_event_handler - register a way to parse an event + * @pevent: the handle to the pevent + * @id: the id of the event to register + * @sys_name: the system name the event belongs to + * @event_name: the name of the event + * @func: the function to call to parse the event information + * @context: the data to be passed to @func + * + * This function allows a developer to override the parsing of + * a given event. If for some reason the default print format + * is not sufficient, this function will register a function + * for an event to be used to parse the data instead. + * + * If @id is >= 0, then it is used to find the event. + * else @sys_name and @event_name are used. + */ +int pevent_register_event_handler(struct pevent *pevent, int id, + const char *sys_name, const char *event_name, + pevent_event_handler_func func, void *context) +{ + struct event_format *event; + struct event_handler *handle; + + event = pevent_search_event(pevent, id, sys_name, event_name); + if (event == NULL) + goto not_found; + + pr_stat("overriding event (%d) %s:%s with new print handler", + event->id, event->system, event->name); + + event->handler = func; + event->context = context; + return 0; + + not_found: + /* Save for later use. */ + handle = calloc(1, sizeof(*handle)); + if (!handle) { + do_warning("Failed to allocate event handler"); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } + + handle->id = id; + if (event_name) + handle->event_name = strdup(event_name); + if (sys_name) + handle->sys_name = strdup(sys_name); + + if ((event_name && !handle->event_name) || + (sys_name && !handle->sys_name)) { + do_warning("Failed to allocate event/sys name"); + free((void *)handle->event_name); + free((void *)handle->sys_name); + free(handle); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } + + handle->func = func; + handle->next = pevent->handlers; + pevent->handlers = handle; + handle->context = context; + + return -1; +} + +static int handle_matches(struct event_handler *handler, int id, + const char *sys_name, const char *event_name, + pevent_event_handler_func func, void *context) +{ + if (id >= 0 && id != handler->id) + return 0; + + if (event_name && (strcmp(event_name, handler->event_name) != 0)) + return 0; + + if (sys_name && (strcmp(sys_name, handler->sys_name) != 0)) + return 0; + + if (func != handler->func || context != handler->context) + return 0; + + return 1; +} + +/** + * pevent_unregister_event_handler - unregister an existing event handler + * @pevent: the handle to the pevent + * @id: the id of the event to unregister + * @sys_name: the system name the handler belongs to + * @event_name: the name of the event handler + * @func: the function to call to parse the event information + * @context: the data to be passed to @func + * + * This function removes existing event handler (parser). + * + * If @id is >= 0, then it is used to find the event. + * else @sys_name and @event_name are used. + * + * Returns 0 if handler was removed successfully, -1 if event was not found. + */ +int pevent_unregister_event_handler(struct pevent *pevent, int id, + const char *sys_name, const char *event_name, + pevent_event_handler_func func, void *context) +{ + struct event_format *event; + struct event_handler *handle; + struct event_handler **next; + + event = pevent_search_event(pevent, id, sys_name, event_name); + if (event == NULL) + goto not_found; + + if (event->handler == func && event->context == context) { + pr_stat("removing override handler for event (%d) %s:%s. Going back to default handler.", + event->id, event->system, event->name); + + event->handler = NULL; + event->context = NULL; + return 0; + } + +not_found: + for (next = &pevent->handlers; *next; next = &(*next)->next) { + handle = *next; + if (handle_matches(handle, id, sys_name, event_name, + func, context)) + break; + } + + if (!(*next)) + return -1; + + *next = handle->next; + free_handler(handle); + + return 0; +} + +/** + * pevent_alloc - create a pevent handle + */ +struct pevent *pevent_alloc(void) +{ + struct pevent *pevent = calloc(1, sizeof(*pevent)); + + if (pevent) + pevent->ref_count = 1; + + return pevent; +} + +void pevent_ref(struct pevent *pevent) +{ + pevent->ref_count++; +} + +void pevent_free_format_field(struct format_field *field) +{ + free(field->type); + free(field->name); + free(field); +} + +static void free_format_fields(struct format_field *field) +{ + struct format_field *next; + + while (field) { + next = field->next; + pevent_free_format_field(field); + field = next; + } +} + +static void free_formats(struct format *format) +{ + free_format_fields(format->common_fields); + free_format_fields(format->fields); +} + +void pevent_free_format(struct event_format *event) +{ + free(event->name); + free(event->system); + + free_formats(&event->format); + + free(event->print_fmt.format); + free_args(event->print_fmt.args); + + free(event); +} + +/** + * pevent_free - free a pevent handle + * @pevent: the pevent handle to free + */ +void pevent_free(struct pevent *pevent) +{ + struct cmdline_list *cmdlist, *cmdnext; + struct func_list *funclist, *funcnext; + struct printk_list *printklist, *printknext; + struct pevent_function_handler *func_handler; + struct event_handler *handle; + int i; + + if (!pevent) + return; + + cmdlist = pevent->cmdlist; + funclist = pevent->funclist; + printklist = pevent->printklist; + + pevent->ref_count--; + if (pevent->ref_count) + return; + + if (pevent->cmdlines) { + for (i = 0; i < pevent->cmdline_count; i++) + free(pevent->cmdlines[i].comm); + free(pevent->cmdlines); + } + + while (cmdlist) { + cmdnext = cmdlist->next; + free(cmdlist->comm); + free(cmdlist); + cmdlist = cmdnext; + } + + if (pevent->func_map) { + for (i = 0; i < (int)pevent->func_count; i++) { + free(pevent->func_map[i].func); + free(pevent->func_map[i].mod); + } + free(pevent->func_map); + } + + while (funclist) { + funcnext = funclist->next; + free(funclist->func); + free(funclist->mod); + free(funclist); + funclist = funcnext; + } + + while (pevent->func_handlers) { + func_handler = pevent->func_handlers; + pevent->func_handlers = func_handler->next; + free_func_handle(func_handler); + } + + if (pevent->printk_map) { + for (i = 0; i < (int)pevent->printk_count; i++) + free(pevent->printk_map[i].printk); + free(pevent->printk_map); + } + + while (printklist) { + printknext = printklist->next; + free(printklist->printk); + free(printklist); + printklist = printknext; + } + + for (i = 0; i < pevent->nr_events; i++) + pevent_free_format(pevent->events[i]); + + while (pevent->handlers) { + handle = pevent->handlers; + pevent->handlers = handle->next; + free_handler(handle); + } + + free(pevent->trace_clock); + free(pevent->events); + free(pevent->sort_events); + + free(pevent); +} + +void pevent_unref(struct pevent *pevent) +{ + pevent_free(pevent); +} diff --git a/kernel/tools/lib/traceevent/event-parse.h b/kernel/tools/lib/traceevent/event-parse.h new file mode 100644 index 000000000..86a5839fb --- /dev/null +++ b/kernel/tools/lib/traceevent/event-parse.h @@ -0,0 +1,964 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#ifndef _PARSE_EVENTS_H +#define _PARSE_EVENTS_H + +#include +#include +#include +#include +#include + +#ifndef __maybe_unused +#define __maybe_unused __attribute__((unused)) +#endif + +/* ----------------------- trace_seq ----------------------- */ + + +#ifndef TRACE_SEQ_BUF_SIZE +#define TRACE_SEQ_BUF_SIZE 4096 +#endif + +#ifndef DEBUG_RECORD +#define DEBUG_RECORD 0 +#endif + +struct pevent_record { + unsigned long long ts; + unsigned long long offset; + long long missed_events; /* buffer dropped events before */ + int record_size; /* size of binary record */ + int size; /* size of data */ + void *data; + int cpu; + int ref_count; + int locked; /* Do not free, even if ref_count is zero */ + void *priv; +#if DEBUG_RECORD + struct pevent_record *prev; + struct pevent_record *next; + long alloc_addr; +#endif +}; + +enum trace_seq_fail { + TRACE_SEQ__GOOD, + TRACE_SEQ__BUFFER_POISONED, + TRACE_SEQ__MEM_ALLOC_FAILED, +}; + +/* + * Trace sequences are used to allow a function to call several other functions + * to create a string of data to use (up to a max of PAGE_SIZE). + */ + +struct trace_seq { + char *buffer; + unsigned int buffer_size; + unsigned int len; + unsigned int readpos; + enum trace_seq_fail state; +}; + +void trace_seq_init(struct trace_seq *s); +void trace_seq_reset(struct trace_seq *s); +void trace_seq_destroy(struct trace_seq *s); + +extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); +extern int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) + __attribute__ ((format (printf, 2, 0))); + +extern int trace_seq_puts(struct trace_seq *s, const char *str); +extern int trace_seq_putc(struct trace_seq *s, unsigned char c); + +extern void trace_seq_terminate(struct trace_seq *s); + +extern int trace_seq_do_fprintf(struct trace_seq *s, FILE *fp); +extern int trace_seq_do_printf(struct trace_seq *s); + + +/* ----------------------- pevent ----------------------- */ + +struct pevent; +struct event_format; + +typedef int (*pevent_event_handler_func)(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, + void *context); + +typedef int (*pevent_plugin_load_func)(struct pevent *pevent); +typedef int (*pevent_plugin_unload_func)(struct pevent *pevent); + +struct pevent_plugin_option { + struct pevent_plugin_option *next; + void *handle; + char *file; + char *name; + char *plugin_alias; + char *description; + const char *value; + void *priv; + int set; +}; + +/* + * Plugin hooks that can be called: + * + * PEVENT_PLUGIN_LOADER: (required) + * The function name to initialized the plugin. + * + * int PEVENT_PLUGIN_LOADER(struct pevent *pevent) + * + * PEVENT_PLUGIN_UNLOADER: (optional) + * The function called just before unloading + * + * int PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) + * + * PEVENT_PLUGIN_OPTIONS: (optional) + * Plugin options that can be set before loading + * + * struct pevent_plugin_option PEVENT_PLUGIN_OPTIONS[] = { + * { + * .name = "option-name", + * .plugin_alias = "overide-file-name", (optional) + * .description = "description of option to show users", + * }, + * { + * .name = NULL, + * }, + * }; + * + * Array must end with .name = NULL; + * + * + * .plugin_alias is used to give a shorter name to access + * the vairable. Useful if a plugin handles more than one event. + * + * If .value is not set, then it is considered a boolean and only + * .set will be processed. If .value is defined, then it is considered + * a string option and .set will be ignored. + * + * PEVENT_PLUGIN_ALIAS: (optional) + * The name to use for finding options (uses filename if not defined) + */ +#define PEVENT_PLUGIN_LOADER pevent_plugin_loader +#define PEVENT_PLUGIN_UNLOADER pevent_plugin_unloader +#define PEVENT_PLUGIN_OPTIONS pevent_plugin_options +#define PEVENT_PLUGIN_ALIAS pevent_plugin_alias +#define _MAKE_STR(x) #x +#define MAKE_STR(x) _MAKE_STR(x) +#define PEVENT_PLUGIN_LOADER_NAME MAKE_STR(PEVENT_PLUGIN_LOADER) +#define PEVENT_PLUGIN_UNLOADER_NAME MAKE_STR(PEVENT_PLUGIN_UNLOADER) +#define PEVENT_PLUGIN_OPTIONS_NAME MAKE_STR(PEVENT_PLUGIN_OPTIONS) +#define PEVENT_PLUGIN_ALIAS_NAME MAKE_STR(PEVENT_PLUGIN_ALIAS) + +#define NSECS_PER_SEC 1000000000ULL +#define NSECS_PER_USEC 1000ULL + +enum format_flags { + FIELD_IS_ARRAY = 1, + FIELD_IS_POINTER = 2, + FIELD_IS_SIGNED = 4, + FIELD_IS_STRING = 8, + FIELD_IS_DYNAMIC = 16, + FIELD_IS_LONG = 32, + FIELD_IS_FLAG = 64, + FIELD_IS_SYMBOLIC = 128, +}; + +struct format_field { + struct format_field *next; + struct event_format *event; + char *type; + char *name; + int offset; + int size; + unsigned int arraylen; + unsigned int elementsize; + unsigned long flags; +}; + +struct format { + int nr_common; + int nr_fields; + struct format_field *common_fields; + struct format_field *fields; +}; + +struct print_arg_atom { + char *atom; +}; + +struct print_arg_string { + char *string; + int offset; +}; + +struct print_arg_bitmask { + char *bitmask; + int offset; +}; + +struct print_arg_field { + char *name; + struct format_field *field; +}; + +struct print_flag_sym { + struct print_flag_sym *next; + char *value; + char *str; +}; + +struct print_arg_typecast { + char *type; + struct print_arg *item; +}; + +struct print_arg_flags { + struct print_arg *field; + char *delim; + struct print_flag_sym *flags; +}; + +struct print_arg_symbol { + struct print_arg *field; + struct print_flag_sym *symbols; +}; + +struct print_arg_hex { + struct print_arg *field; + struct print_arg *size; +}; + +struct print_arg_int_array { + struct print_arg *field; + struct print_arg *count; + struct print_arg *el_size; +}; + +struct print_arg_dynarray { + struct format_field *field; + struct print_arg *index; +}; + +struct print_arg; + +struct print_arg_op { + char *op; + int prio; + struct print_arg *left; + struct print_arg *right; +}; + +struct pevent_function_handler; + +struct print_arg_func { + struct pevent_function_handler *func; + struct print_arg *args; +}; + +enum print_arg_type { + PRINT_NULL, + PRINT_ATOM, + PRINT_FIELD, + PRINT_FLAGS, + PRINT_SYMBOL, + PRINT_HEX, + PRINT_INT_ARRAY, + PRINT_TYPE, + PRINT_STRING, + PRINT_BSTRING, + PRINT_DYNAMIC_ARRAY, + PRINT_OP, + PRINT_FUNC, + PRINT_BITMASK, +}; + +struct print_arg { + struct print_arg *next; + enum print_arg_type type; + union { + struct print_arg_atom atom; + struct print_arg_field field; + struct print_arg_typecast typecast; + struct print_arg_flags flags; + struct print_arg_symbol symbol; + struct print_arg_hex hex; + struct print_arg_int_array int_array; + struct print_arg_func func; + struct print_arg_string string; + struct print_arg_bitmask bitmask; + struct print_arg_op op; + struct print_arg_dynarray dynarray; + }; +}; + +struct print_fmt { + char *format; + struct print_arg *args; +}; + +struct event_format { + struct pevent *pevent; + char *name; + int id; + int flags; + struct format format; + struct print_fmt print_fmt; + char *system; + pevent_event_handler_func handler; + void *context; +}; + +enum { + EVENT_FL_ISFTRACE = 0x01, + EVENT_FL_ISPRINT = 0x02, + EVENT_FL_ISBPRINT = 0x04, + EVENT_FL_ISFUNCENT = 0x10, + EVENT_FL_ISFUNCRET = 0x20, + EVENT_FL_NOHANDLE = 0x40, + EVENT_FL_PRINTRAW = 0x80, + + EVENT_FL_FAILED = 0x80000000 +}; + +enum event_sort_type { + EVENT_SORT_ID, + EVENT_SORT_NAME, + EVENT_SORT_SYSTEM, +}; + +enum event_type { + EVENT_ERROR, + EVENT_NONE, + EVENT_SPACE, + EVENT_NEWLINE, + EVENT_OP, + EVENT_DELIM, + EVENT_ITEM, + EVENT_DQUOTE, + EVENT_SQUOTE, +}; + +typedef unsigned long long (*pevent_func_handler)(struct trace_seq *s, + unsigned long long *args); + +enum pevent_func_arg_type { + PEVENT_FUNC_ARG_VOID, + PEVENT_FUNC_ARG_INT, + PEVENT_FUNC_ARG_LONG, + PEVENT_FUNC_ARG_STRING, + PEVENT_FUNC_ARG_PTR, + PEVENT_FUNC_ARG_MAX_TYPES +}; + +enum pevent_flag { + PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ + PEVENT_DISABLE_SYS_PLUGINS = 1 << 1, + PEVENT_DISABLE_PLUGINS = 1 << 2, +}; + +#define PEVENT_ERRORS \ + _PE(MEM_ALLOC_FAILED, "failed to allocate memory"), \ + _PE(PARSE_EVENT_FAILED, "failed to parse event"), \ + _PE(READ_ID_FAILED, "failed to read event id"), \ + _PE(READ_FORMAT_FAILED, "failed to read event format"), \ + _PE(READ_PRINT_FAILED, "failed to read event print fmt"), \ + _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\ + _PE(INVALID_ARG_TYPE, "invalid argument type"), \ + _PE(INVALID_EXP_TYPE, "invalid expression type"), \ + _PE(INVALID_OP_TYPE, "invalid operator type"), \ + _PE(INVALID_EVENT_NAME, "invalid event name"), \ + _PE(EVENT_NOT_FOUND, "no event found"), \ + _PE(SYNTAX_ERROR, "syntax error"), \ + _PE(ILLEGAL_RVALUE, "illegal rvalue"), \ + _PE(ILLEGAL_LVALUE, "illegal lvalue for string comparison"), \ + _PE(INVALID_REGEX, "regex did not compute"), \ + _PE(ILLEGAL_STRING_CMP, "illegal comparison for string"), \ + _PE(ILLEGAL_INTEGER_CMP,"illegal comparison for integer"), \ + _PE(REPARENT_NOT_OP, "cannot reparent other than OP"), \ + _PE(REPARENT_FAILED, "failed to reparent filter OP"), \ + _PE(BAD_FILTER_ARG, "bad arg in filter tree"), \ + _PE(UNEXPECTED_TYPE, "unexpected type (not a value)"), \ + _PE(ILLEGAL_TOKEN, "illegal token"), \ + _PE(INVALID_PAREN, "open parenthesis cannot come here"), \ + _PE(UNBALANCED_PAREN, "unbalanced number of parenthesis"), \ + _PE(UNKNOWN_TOKEN, "unknown token"), \ + _PE(FILTER_NOT_FOUND, "no filter found"), \ + _PE(NOT_A_NUMBER, "must have number field"), \ + _PE(NO_FILTER, "no filters exists"), \ + _PE(FILTER_MISS, "record does not match to filter") + +#undef _PE +#define _PE(__code, __str) PEVENT_ERRNO__ ## __code +enum pevent_errno { + PEVENT_ERRNO__SUCCESS = 0, + PEVENT_ERRNO__FILTER_MATCH = PEVENT_ERRNO__SUCCESS, + + /* + * Choose an arbitrary negative big number not to clash with standard + * errno since SUS requires the errno has distinct positive values. + * See 'Issue 6' in the link below. + * + * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html + */ + __PEVENT_ERRNO__START = -100000, + + PEVENT_ERRORS, + + __PEVENT_ERRNO__END, +}; +#undef _PE + +struct plugin_list; + +#define INVALID_PLUGIN_LIST_OPTION ((char **)((unsigned long)-1)) + +struct plugin_list *traceevent_load_plugins(struct pevent *pevent); +void traceevent_unload_plugins(struct plugin_list *plugin_list, + struct pevent *pevent); +char **traceevent_plugin_list_options(void); +void traceevent_plugin_free_options_list(char **list); +int traceevent_plugin_add_options(const char *name, + struct pevent_plugin_option *options); +void traceevent_plugin_remove_options(struct pevent_plugin_option *options); +void traceevent_print_plugins(struct trace_seq *s, + const char *prefix, const char *suffix, + const struct plugin_list *list); + +struct cmdline; +struct cmdline_list; +struct func_map; +struct func_list; +struct event_handler; + +struct pevent { + int ref_count; + + int header_page_ts_offset; + int header_page_ts_size; + int header_page_size_offset; + int header_page_size_size; + int header_page_data_offset; + int header_page_data_size; + int header_page_overwrite; + + int file_bigendian; + int host_bigendian; + + int latency_format; + + int old_format; + + int cpus; + int long_size; + int page_size; + + struct cmdline *cmdlines; + struct cmdline_list *cmdlist; + int cmdline_count; + + struct func_map *func_map; + struct func_list *funclist; + unsigned int func_count; + + struct printk_map *printk_map; + struct printk_list *printklist; + unsigned int printk_count; + + + struct event_format **events; + int nr_events; + struct event_format **sort_events; + enum event_sort_type last_type; + + int type_offset; + int type_size; + + int pid_offset; + int pid_size; + + int pc_offset; + int pc_size; + + int flags_offset; + int flags_size; + + int ld_offset; + int ld_size; + + int print_raw; + + int test_filters; + + int flags; + + struct format_field *bprint_ip_field; + struct format_field *bprint_fmt_field; + struct format_field *bprint_buf_field; + + struct event_handler *handlers; + struct pevent_function_handler *func_handlers; + + /* cache */ + struct event_format *last_event; + + char *trace_clock; +}; + +static inline void pevent_set_flag(struct pevent *pevent, int flag) +{ + pevent->flags |= flag; +} + +static inline unsigned short +__data2host2(struct pevent *pevent, unsigned short data) +{ + unsigned short swap; + + if (pevent->host_bigendian == pevent->file_bigendian) + return data; + + swap = ((data & 0xffULL) << 8) | + ((data & (0xffULL << 8)) >> 8); + + return swap; +} + +static inline unsigned int +__data2host4(struct pevent *pevent, unsigned int data) +{ + unsigned int swap; + + if (pevent->host_bigendian == pevent->file_bigendian) + return data; + + swap = ((data & 0xffULL) << 24) | + ((data & (0xffULL << 8)) << 8) | + ((data & (0xffULL << 16)) >> 8) | + ((data & (0xffULL << 24)) >> 24); + + return swap; +} + +static inline unsigned long long +__data2host8(struct pevent *pevent, unsigned long long data) +{ + unsigned long long swap; + + if (pevent->host_bigendian == pevent->file_bigendian) + return data; + + swap = ((data & 0xffULL) << 56) | + ((data & (0xffULL << 8)) << 40) | + ((data & (0xffULL << 16)) << 24) | + ((data & (0xffULL << 24)) << 8) | + ((data & (0xffULL << 32)) >> 8) | + ((data & (0xffULL << 40)) >> 24) | + ((data & (0xffULL << 48)) >> 40) | + ((data & (0xffULL << 56)) >> 56); + + return swap; +} + +#define data2host2(pevent, ptr) __data2host2(pevent, *(unsigned short *)(ptr)) +#define data2host4(pevent, ptr) __data2host4(pevent, *(unsigned int *)(ptr)) +#define data2host8(pevent, ptr) \ +({ \ + unsigned long long __val; \ + \ + memcpy(&__val, (ptr), sizeof(unsigned long long)); \ + __data2host8(pevent, __val); \ +}) + +static inline int traceevent_host_bigendian(void) +{ + unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; + unsigned int val; + + memcpy(&val, str, 4); + return val == 0x01020304; +} + +/* taken from kernel/trace/trace.h */ +enum trace_flag_type { + TRACE_FLAG_IRQS_OFF = 0x01, + TRACE_FLAG_IRQS_NOSUPPORT = 0x02, + TRACE_FLAG_NEED_RESCHED = 0x04, + TRACE_FLAG_HARDIRQ = 0x08, + TRACE_FLAG_SOFTIRQ = 0x10, +}; + +int pevent_register_comm(struct pevent *pevent, const char *comm, int pid); +int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock); +int pevent_register_function(struct pevent *pevent, char *name, + unsigned long long addr, char *mod); +int pevent_register_print_string(struct pevent *pevent, const char *fmt, + unsigned long long addr); +int pevent_pid_is_registered(struct pevent *pevent, int pid); + +void pevent_print_event(struct pevent *pevent, struct trace_seq *s, + struct pevent_record *record, bool use_trace_clock); + +int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, + int long_size); + +enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, + unsigned long size, const char *sys); +enum pevent_errno pevent_parse_format(struct pevent *pevent, + struct event_format **eventp, + const char *buf, + unsigned long size, const char *sys); +void pevent_free_format(struct event_format *event); +void pevent_free_format_field(struct format_field *field); + +void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, + const char *name, struct pevent_record *record, + int *len, int err); + +int pevent_get_field_val(struct trace_seq *s, struct event_format *event, + const char *name, struct pevent_record *record, + unsigned long long *val, int err); +int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event, + const char *name, struct pevent_record *record, + unsigned long long *val, int err); +int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event, + const char *name, struct pevent_record *record, + unsigned long long *val, int err); + +int pevent_print_num_field(struct trace_seq *s, const char *fmt, + struct event_format *event, const char *name, + struct pevent_record *record, int err); + +int pevent_print_func_field(struct trace_seq *s, const char *fmt, + struct event_format *event, const char *name, + struct pevent_record *record, int err); + +int pevent_register_event_handler(struct pevent *pevent, int id, + const char *sys_name, const char *event_name, + pevent_event_handler_func func, void *context); +int pevent_unregister_event_handler(struct pevent *pevent, int id, + const char *sys_name, const char *event_name, + pevent_event_handler_func func, void *context); +int pevent_register_print_function(struct pevent *pevent, + pevent_func_handler func, + enum pevent_func_arg_type ret_type, + char *name, ...); +int pevent_unregister_print_function(struct pevent *pevent, + pevent_func_handler func, char *name); + +struct format_field *pevent_find_common_field(struct event_format *event, const char *name); +struct format_field *pevent_find_field(struct event_format *event, const char *name); +struct format_field *pevent_find_any_field(struct event_format *event, const char *name); + +const char *pevent_find_function(struct pevent *pevent, unsigned long long addr); +unsigned long long +pevent_find_function_address(struct pevent *pevent, unsigned long long addr); +unsigned long long pevent_read_number(struct pevent *pevent, const void *ptr, int size); +int pevent_read_number_field(struct format_field *field, const void *data, + unsigned long long *value); + +struct event_format *pevent_find_event(struct pevent *pevent, int id); + +struct event_format * +pevent_find_event_by_name(struct pevent *pevent, const char *sys, const char *name); + +void pevent_data_lat_fmt(struct pevent *pevent, + struct trace_seq *s, struct pevent_record *record); +int pevent_data_type(struct pevent *pevent, struct pevent_record *rec); +struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type); +int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec); +const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); +struct cmdline; +struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *comm, + struct cmdline *next); +int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline); + +void pevent_event_info(struct trace_seq *s, struct event_format *event, + struct pevent_record *record); +int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, + char *buf, size_t buflen); + +struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type); +struct format_field **pevent_event_common_fields(struct event_format *event); +struct format_field **pevent_event_fields(struct event_format *event); + +static inline int pevent_get_cpus(struct pevent *pevent) +{ + return pevent->cpus; +} + +static inline void pevent_set_cpus(struct pevent *pevent, int cpus) +{ + pevent->cpus = cpus; +} + +static inline int pevent_get_long_size(struct pevent *pevent) +{ + return pevent->long_size; +} + +static inline void pevent_set_long_size(struct pevent *pevent, int long_size) +{ + pevent->long_size = long_size; +} + +static inline int pevent_get_page_size(struct pevent *pevent) +{ + return pevent->page_size; +} + +static inline void pevent_set_page_size(struct pevent *pevent, int _page_size) +{ + pevent->page_size = _page_size; +} + +static inline int pevent_is_file_bigendian(struct pevent *pevent) +{ + return pevent->file_bigendian; +} + +static inline void pevent_set_file_bigendian(struct pevent *pevent, int endian) +{ + pevent->file_bigendian = endian; +} + +static inline int pevent_is_host_bigendian(struct pevent *pevent) +{ + return pevent->host_bigendian; +} + +static inline void pevent_set_host_bigendian(struct pevent *pevent, int endian) +{ + pevent->host_bigendian = endian; +} + +static inline int pevent_is_latency_format(struct pevent *pevent) +{ + return pevent->latency_format; +} + +static inline void pevent_set_latency_format(struct pevent *pevent, int lat) +{ + pevent->latency_format = lat; +} + +struct pevent *pevent_alloc(void); +void pevent_free(struct pevent *pevent); +void pevent_ref(struct pevent *pevent); +void pevent_unref(struct pevent *pevent); + +/* access to the internal parser */ +void pevent_buffer_init(const char *buf, unsigned long long size); +enum event_type pevent_read_token(char **tok); +void pevent_free_token(char *token); +int pevent_peek_char(void); +const char *pevent_get_input_buf(void); +unsigned long long pevent_get_input_buf_ptr(void); + +/* for debugging */ +void pevent_print_funcs(struct pevent *pevent); +void pevent_print_printk(struct pevent *pevent); + +/* ----------------------- filtering ----------------------- */ + +enum filter_boolean_type { + FILTER_FALSE, + FILTER_TRUE, +}; + +enum filter_op_type { + FILTER_OP_AND = 1, + FILTER_OP_OR, + FILTER_OP_NOT, +}; + +enum filter_cmp_type { + FILTER_CMP_NONE, + FILTER_CMP_EQ, + FILTER_CMP_NE, + FILTER_CMP_GT, + FILTER_CMP_LT, + FILTER_CMP_GE, + FILTER_CMP_LE, + FILTER_CMP_MATCH, + FILTER_CMP_NOT_MATCH, + FILTER_CMP_REGEX, + FILTER_CMP_NOT_REGEX, +}; + +enum filter_exp_type { + FILTER_EXP_NONE, + FILTER_EXP_ADD, + FILTER_EXP_SUB, + FILTER_EXP_MUL, + FILTER_EXP_DIV, + FILTER_EXP_MOD, + FILTER_EXP_RSHIFT, + FILTER_EXP_LSHIFT, + FILTER_EXP_AND, + FILTER_EXP_OR, + FILTER_EXP_XOR, + FILTER_EXP_NOT, +}; + +enum filter_arg_type { + FILTER_ARG_NONE, + FILTER_ARG_BOOLEAN, + FILTER_ARG_VALUE, + FILTER_ARG_FIELD, + FILTER_ARG_EXP, + FILTER_ARG_OP, + FILTER_ARG_NUM, + FILTER_ARG_STR, +}; + +enum filter_value_type { + FILTER_NUMBER, + FILTER_STRING, + FILTER_CHAR +}; + +struct fliter_arg; + +struct filter_arg_boolean { + enum filter_boolean_type value; +}; + +struct filter_arg_field { + struct format_field *field; +}; + +struct filter_arg_value { + enum filter_value_type type; + union { + char *str; + unsigned long long val; + }; +}; + +struct filter_arg_op { + enum filter_op_type type; + struct filter_arg *left; + struct filter_arg *right; +}; + +struct filter_arg_exp { + enum filter_exp_type type; + struct filter_arg *left; + struct filter_arg *right; +}; + +struct filter_arg_num { + enum filter_cmp_type type; + struct filter_arg *left; + struct filter_arg *right; +}; + +struct filter_arg_str { + enum filter_cmp_type type; + struct format_field *field; + char *val; + char *buffer; + regex_t reg; +}; + +struct filter_arg { + enum filter_arg_type type; + union { + struct filter_arg_boolean boolean; + struct filter_arg_field field; + struct filter_arg_value value; + struct filter_arg_op op; + struct filter_arg_exp exp; + struct filter_arg_num num; + struct filter_arg_str str; + }; +}; + +struct filter_type { + int event_id; + struct event_format *event; + struct filter_arg *filter; +}; + +#define PEVENT_FILTER_ERROR_BUFSZ 1024 + +struct event_filter { + struct pevent *pevent; + int filters; + struct filter_type *event_filters; + char error_buffer[PEVENT_FILTER_ERROR_BUFSZ]; +}; + +struct event_filter *pevent_filter_alloc(struct pevent *pevent); + +/* for backward compatibility */ +#define FILTER_NONE PEVENT_ERRNO__NO_FILTER +#define FILTER_NOEXIST PEVENT_ERRNO__FILTER_NOT_FOUND +#define FILTER_MISS PEVENT_ERRNO__FILTER_MISS +#define FILTER_MATCH PEVENT_ERRNO__FILTER_MATCH + +enum filter_trivial_type { + FILTER_TRIVIAL_FALSE, + FILTER_TRIVIAL_TRUE, + FILTER_TRIVIAL_BOTH, +}; + +enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, + const char *filter_str); + +enum pevent_errno pevent_filter_match(struct event_filter *filter, + struct pevent_record *record); + +int pevent_filter_strerror(struct event_filter *filter, enum pevent_errno err, + char *buf, size_t buflen); + +int pevent_event_filtered(struct event_filter *filter, + int event_id); + +void pevent_filter_reset(struct event_filter *filter); + +int pevent_filter_clear_trivial(struct event_filter *filter, + enum filter_trivial_type type); + +void pevent_filter_free(struct event_filter *filter); + +char *pevent_filter_make_string(struct event_filter *filter, int event_id); + +int pevent_filter_remove_event(struct event_filter *filter, + int event_id); + +int pevent_filter_event_has_trivial(struct event_filter *filter, + int event_id, + enum filter_trivial_type type); + +int pevent_filter_copy(struct event_filter *dest, struct event_filter *source); + +int pevent_update_trivial(struct event_filter *dest, struct event_filter *source, + enum filter_trivial_type type); + +int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2); + +#endif /* _PARSE_EVENTS_H */ diff --git a/kernel/tools/lib/traceevent/event-plugin.c b/kernel/tools/lib/traceevent/event-plugin.c new file mode 100644 index 000000000..a16756ae3 --- /dev/null +++ b/kernel/tools/lib/traceevent/event-plugin.c @@ -0,0 +1,464 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "event-parse.h" +#include "event-utils.h" + +#define LOCAL_PLUGIN_DIR ".traceevent/plugins" + +static struct registered_plugin_options { + struct registered_plugin_options *next; + struct pevent_plugin_option *options; +} *registered_options; + +static struct trace_plugin_options { + struct trace_plugin_options *next; + char *plugin; + char *option; + char *value; +} *trace_plugin_options; + +struct plugin_list { + struct plugin_list *next; + char *name; + void *handle; +}; + +static void lower_case(char *str) +{ + if (!str) + return; + for (; *str; str++) + *str = tolower(*str); +} + +static int update_option_value(struct pevent_plugin_option *op, const char *val) +{ + char *op_val; + + if (!val) { + /* toggle, only if option is boolean */ + if (op->value) + /* Warn? */ + return 0; + op->set ^= 1; + return 0; + } + + /* + * If the option has a value then it takes a string + * otherwise the option is a boolean. + */ + if (op->value) { + op->value = val; + return 0; + } + + /* Option is boolean, must be either "1", "0", "true" or "false" */ + + op_val = strdup(val); + if (!op_val) + return -1; + lower_case(op_val); + + if (strcmp(val, "1") == 0 || strcmp(val, "true") == 0) + op->set = 1; + else if (strcmp(val, "0") == 0 || strcmp(val, "false") == 0) + op->set = 0; + free(op_val); + + return 0; +} + +/** + * traceevent_plugin_list_options - get list of plugin options + * + * Returns an array of char strings that list the currently registered + * plugin options in the format of :