ktap.c is ktapvm kernel module main entry,
it init ktap kernel module, create 'ktap' debugfs directory.

Userspace tool send ioctl command to '/sys/kernel/debug/ktap/ktapvm' file,
to control ktap runtime.

It will read bytecode trunk, validate and execute bytecode.

kp_vm_new_state
    kp_bcread
        kp_vm_validate_code
            kp_vm_call_proto

Signed-off-by: Jovi Zhangwei <jovi.zhang...@gmail.com>
---
 kernel/trace/ktap/ktap.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++
 kernel/trace/ktap/ktap.h | 176 ++++++++++++++++++++++++++++++++
 2 files changed, 431 insertions(+)
 create mode 100644 kernel/trace/ktap/ktap.c
 create mode 100644 kernel/trace/ktap/ktap.h

diff --git a/kernel/trace/ktap/ktap.c b/kernel/trace/ktap/ktap.c
new file mode 100644
index 0000000..855af09
--- /dev/null
+++ b/kernel/trace/ktap/ktap.c
@@ -0,0 +1,255 @@
+/*
+ * ktap.c - ktapvm kernel module main entry
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2014 Jovi Zhangwei <jovi.zhang...@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * this file is the first file to be compile, add CONFIG_ checking in here.
+ * See Requirements in doc/tutorial.md
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/anon_inodes.h>
+#include <linux/debugfs.h>
+#include <linux/vmalloc.h>
+#include <uapi/ktap/ktap_types.h>
+#include "ktap.h"
+#include "kp_bcread.h"
+#include "kp_vm.h"
+
+/* 
+ * gettimeofday_ns: common helper function
+ * TODO: make getnstimeofday safe called in probe context, there have
+ * seq lock in getnstimeofday.
+ * (Systemtap fix this by introduce its own timekeeping code)
+ */
+long gettimeofday_ns(void)
+{
+       struct timespec now;
+
+       getnstimeofday(&now);
+       return now.tv_sec * NSEC_PER_SEC + now.tv_nsec;
+}
+
+static int load_trunk(ktap_option_t *parm, unsigned long **buff)
+{
+       unsigned long *vmstart;
+
+       if (parm->trunk_len > 4096)
+               return -EINVAL;
+
+       vmstart = vmalloc(parm->trunk_len);
+       if (!vmstart)
+               return -ENOMEM;
+
+       if (copy_from_user(vmstart, (void __user *)parm->trunk,
+                          parm->trunk_len)) {
+               vfree(vmstart);
+               return -EFAULT;
+       }
+
+       *buff = vmstart;
+       return 0;
+}
+
+static struct dentry *kp_dir_dentry;
+
+/* Ktap Main Entry */
+static int ktap_main(struct file *file, ktap_option_t *parm)
+{
+       unsigned long *buff = NULL;
+       ktap_state_t *ks;
+       ktap_proto_t *pt;
+       long start_time, delta_time;
+       int ret;
+
+       start_time = gettimeofday_ns();
+
+       ks = kp_vm_new_state(parm, kp_dir_dentry);
+       if (unlikely(!ks))
+               return -ENOEXEC;
+
+       file->private_data = ks;
+
+       ret = load_trunk(parm, &buff);
+       if (ret) {
+               kp_error(ks, "cannot load file\n");
+               goto out;
+       }
+
+       pt = kp_bcread(ks, (unsigned char *)buff, parm->trunk_len);
+
+       vfree(buff);
+
+       if (pt) {
+               /* validate byte code */
+               if (kp_vm_validate_code(ks, pt, ks->stack))
+                       goto out;
+
+               delta_time = (gettimeofday_ns() - start_time) / NSEC_PER_USEC;
+               kp_verbose_printf(ks, "booting time: %d (us)\n", delta_time);
+
+               /* enter vm */
+               kp_vm_call_proto(ks, pt);
+       }
+
+ out:
+       kp_vm_exit(ks);
+       return ret;
+}
+
+static long ktap_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       ktap_option_t parm;
+
+       switch (cmd) {
+       case KTAP_CMD_IOC_RUN:
+               /* must be root to run ktap script (at least for now) */
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EACCES;
+
+               if (copy_from_user(&parm, (void __user *)arg,
+                                  sizeof(ktap_option_t)))
+                       return -EFAULT;
+
+               return ktap_main(file, &parm);
+       default:
+               return -EINVAL;
+       };
+
+        return 0;
+}
+
+static const struct file_operations ktap_fops = {
+       .llseek                 = no_llseek,
+       .unlocked_ioctl         = ktap_ioctl,
+};
+
+static long ktapvm_ioctl(struct file *file, unsigned int cmd, unsigned long 
arg)
+{
+       int new_fd, err;
+       struct file *new_file;
+
+       new_fd = get_unused_fd();
+       if (new_fd < 0)
+               return new_fd;
+
+       new_file = anon_inode_getfile("[ktap]", &ktap_fops, NULL, O_RDWR);
+       if (IS_ERR(new_file)) {
+               err = PTR_ERR(new_file);
+               put_unused_fd(new_fd);
+               return err;
+       }
+
+       file->private_data = NULL;
+       fd_install(new_fd, new_file);
+       return new_fd;
+}
+
+static const struct file_operations ktapvm_fops = {
+       .owner  = THIS_MODULE,
+       .unlocked_ioctl         = ktapvm_ioctl,
+};
+
+int (*kp_ftrace_profile_set_filter)(struct perf_event *event, int event_id,
+                                   const char *filter_str);
+
+struct syscall_metadata **syscalls_metadata;
+
+/*TODO: kill this function in future */
+static int __init init_dummy_kernel_functions(void)
+{
+       unsigned long *addr;
+
+       /*
+        * ktap need symbol ftrace_profile_set_filter to set event filter, 
+        * export it in future. 
+        */
+#ifdef CONFIG_PPC64
+       kp_ftrace_profile_set_filter =
+               (void *)kallsyms_lookup_name(".ftrace_profile_set_filter");
+#else
+       kp_ftrace_profile_set_filter =
+               (void *)kallsyms_lookup_name("ftrace_profile_set_filter");
+#endif
+       if (!kp_ftrace_profile_set_filter) {
+               pr_err("ktap: cannot lookup ftrace_profile_set_filter "
+                       "in kallsyms\n");
+               return -1;
+       }
+
+       /* use syscalls_metadata for syscall event handling */
+       addr = (void *)kallsyms_lookup_name("syscalls_metadata");
+       if (!addr) {
+               pr_err("ktap: cannot lookup syscalls_metadata in kallsyms\n");
+               return -1;
+       }
+
+       syscalls_metadata = (struct syscall_metadata **)*addr;
+       return 0;
+}
+
+static int __init init_ktap(void)
+{
+       struct dentry *ktapvm_dentry;
+
+       if (init_dummy_kernel_functions())
+               return -1;
+
+       kp_dir_dentry = debugfs_create_dir("ktap", NULL);
+       if (!kp_dir_dentry) {
+               pr_err("ktap: debugfs_create_dir failed\n");
+               return -1;
+       }
+
+       ktapvm_dentry = debugfs_create_file("ktapvm", 0444, kp_dir_dentry, NULL,
+                                           &ktapvm_fops);
+
+       if (!ktapvm_dentry) {
+               pr_err("ktapvm: cannot create ktapvm file\n");
+               debugfs_remove_recursive(kp_dir_dentry);
+               return -1;
+       }
+
+       return 0;
+}
+
+static void __exit exit_ktap(void)
+{
+       debugfs_remove_recursive(kp_dir_dentry);
+}
+
+module_init(init_ktap);
+module_exit(exit_ktap);
+
+MODULE_AUTHOR("Jovi Zhangwei <jovi.zhang...@gmail.com>");
+MODULE_DESCRIPTION("ktap");
+MODULE_LICENSE("GPL");
+
+int kp_max_loop_count = 100000;
+module_param_named(max_loop_count, kp_max_loop_count, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_loop_count, "max loop execution count");
+
diff --git a/kernel/trace/ktap/ktap.h b/kernel/trace/ktap/ktap.h
new file mode 100644
index 0000000..90d1468
--- /dev/null
+++ b/kernel/trace/ktap/ktap.h
@@ -0,0 +1,176 @@
+#ifndef __KTAP_H__
+#define __KTAP_H__
+
+#include <linux/version.h>
+#include <linux/hardirq.h>
+#include <linux/trace_seq.h>
+
+/* for built-in library C function register */
+typedef struct ktap_libfunc {
+        const char *name; /* function name */
+        ktap_cfunction func; /* function pointer */
+} ktap_libfunc_t;
+
+long gettimeofday_ns(void); /* common helper function */
+int kp_lib_init_base(ktap_state_t *ks);
+int kp_lib_init_kdebug(ktap_state_t *ks);
+int kp_lib_init_timer(ktap_state_t *ks);
+int kp_lib_init_table(ktap_state_t *ks);
+int kp_lib_init_ansi(ktap_state_t *ks);
+int kp_lib_init_net(ktap_state_t *ks);
+
+void kp_exit_timers(ktap_state_t *ks);
+void kp_freeupval(ktap_state_t *ks, ktap_upval_t *uv);
+
+extern int (*kp_ftrace_profile_set_filter)(struct perf_event *event,
+                                          int event_id,
+                                          const char *filter_str);
+
+extern struct syscall_metadata **syscalls_metadata;
+
+/* get from kernel/trace/trace.h */
+static __always_inline int trace_get_context_bit(void)
+{
+       int bit;
+
+       if (in_interrupt()) {
+               if (in_nmi())
+                       bit = 0;
+               else if (in_irq())
+                       bit = 1;
+               else
+                       bit = 2;
+       } else
+               bit = 3;
+
+       return bit;
+}
+
+static __always_inline int get_recursion_context(ktap_state_t *ks)
+{
+       int rctx = trace_get_context_bit();
+       int *val = __this_cpu_ptr(G(ks)->recursion_context[rctx]);
+
+       if (*val)
+               return -1;
+
+       *val = true;
+       return rctx;
+}
+
+static inline void put_recursion_context(ktap_state_t *ks, int rctx)
+{
+       int *val = __this_cpu_ptr(G(ks)->recursion_context[rctx]);
+       *val = false;
+}
+
+static inline void *kp_this_cpu_state(ktap_state_t *ks, int rctx)
+{
+       return this_cpu_ptr(G(ks)->percpu_state[rctx]);
+}
+
+static inline void *kp_this_cpu_print_buffer(ktap_state_t *ks)
+{
+       return 
this_cpu_ptr(G(ks)->percpu_print_buffer[trace_get_context_bit()]);
+}
+
+static inline void *kp_this_cpu_temp_buffer(ktap_state_t *ks)
+{
+       return this_cpu_ptr(G(ks)->percpu_temp_buffer[trace_get_context_bit()]);
+}
+
+#define kp_verbose_printf(ks, ...) \
+       if (G(ks)->parm->verbose)       \
+               kp_printf(ks, "[verbose] "__VA_ARGS__);
+
+/* argument operation macro */
+#define kp_arg(ks, idx)        ((ks)->func + (idx))
+#define kp_arg_nr(ks)  ((int)(ks->top - (ks->func + 1)))
+
+#define kp_arg_check(ks, idx, type)                            \
+       do {                                                    \
+               if (unlikely(itype(kp_arg(ks, idx)) != type)) { \
+                       kp_error(ks, "wrong type of argument %d\n", idx);\
+                       return -1;                              \
+               }                                               \
+       } while (0)
+
+#define kp_arg_checkstring(ks, idx)                            \
+       ({                                                      \
+               ktap_val_t *o = kp_arg(ks, idx);                \
+               if (unlikely(!is_string(o))) {                  \
+                       kp_error(ks, "wrong type of argument %d\n", idx); \
+                       return -1;                              \
+               }                                               \
+               svalue(o);                                      \
+       })
+
+#define kp_arg_checkfunction(ks, idx)                          \
+       ({                                                      \
+               ktap_val_t *o = kp_arg(ks, idx);                \
+               if (unlikely(!is_function(o))) {                        \
+                       kp_error(ks, "wrong type of argument %d\n", idx); \
+                       return -1;                              \
+               }                                               \
+               clvalue(o);                                     \
+       })
+
+#define kp_arg_checknumber(ks, idx)                            \
+       ({                                                      \
+               ktap_val_t *o = kp_arg(ks, idx);                \
+               if (unlikely(!is_number(o))) {                  \
+                       kp_error(ks, "wrong type of argument %d\n", idx); \
+                       return -1;                              \
+               }                                               \
+               nvalue(o);                                      \
+       })
+
+#define kp_arg_checkoptnumber(ks, idx, def)                    \
+       ({                                                      \
+               ktap_number n;                                  \
+               if (idx > kp_arg_nr(ks)) {                              \
+                       n = def;                                \
+               } else {                                        \
+                       ktap_val_t *o = kp_arg(ks, idx);        \
+                       if (unlikely(!is_number(o))) {          \
+                               kp_error(ks, "wrong type of argument %d\n", \
+                                            idx);              \
+                               return -1;                      \
+                       }                                       \
+                       n = nvalue(o);                          \
+               }                                               \
+               n;                                              \
+       })
+
+#define kp_error(ks, args...)                  \
+       do {                                    \
+               kp_printf(ks, "error: "args);   \
+               kp_vm_try_to_exit(ks);          \
+               G(ks)->state = KTAP_ERROR;      \
+       } while(0)
+
+
+#define SPRINT_SYMBOL  sprint_symbol_no_offset
+
+extern int kp_max_loop_count;
+
+void kp_printf(ktap_state_t *ks, const char *fmt, ...);
+void __kp_puts(ktap_state_t *ks, const char *str);
+void __kp_bputs(ktap_state_t *ks, const char *str);
+
+#define kp_puts(ks, str) ({                                            \
+       static const char *trace_printk_fmt                             \
+               __attribute__((section("__trace_printk_fmt"))) =        \
+               __builtin_constant_p(str) ? str : NULL;                 \
+                                                                       \
+       if (__builtin_constant_p(str))                                  \
+               __kp_bputs(ks, trace_printk_fmt);               \
+       else                                                            \
+               __kp_puts(ks, str);             \
+})
+
+#define err2msg(em)     (kp_err_allmsg+(int)(em))
+extern const char *kp_err_allmsg;
+
+#endif /* __KTAP_H__ */
+
-- 
1.8.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to