1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 25c9a8750SDmitry Vyukov #define pr_fmt(fmt) "kcov: " fmt 35c9a8750SDmitry Vyukov 436f05ae8SAndrey Ryabinin #define DISABLE_BRANCH_PROFILING 5db862358SKefeng Wang #include <linux/atomic.h> 65c9a8750SDmitry Vyukov #include <linux/compiler.h> 7db862358SKefeng Wang #include <linux/errno.h> 8db862358SKefeng Wang #include <linux/export.h> 95c9a8750SDmitry Vyukov #include <linux/types.h> 105c9a8750SDmitry Vyukov #include <linux/file.h> 115c9a8750SDmitry Vyukov #include <linux/fs.h> 12db862358SKefeng Wang #include <linux/init.h> 135c9a8750SDmitry Vyukov #include <linux/mm.h> 14db862358SKefeng Wang #include <linux/preempt.h> 155c9a8750SDmitry Vyukov #include <linux/printk.h> 16166ad0e1SKefeng Wang #include <linux/sched.h> 175c9a8750SDmitry Vyukov #include <linux/slab.h> 185c9a8750SDmitry Vyukov #include <linux/spinlock.h> 195c9a8750SDmitry Vyukov #include <linux/vmalloc.h> 205c9a8750SDmitry Vyukov #include <linux/debugfs.h> 215c9a8750SDmitry Vyukov #include <linux/uaccess.h> 225c9a8750SDmitry Vyukov #include <linux/kcov.h> 23*39e07cb6SElena Reshetova #include <linux/refcount.h> 244983f0abSAlexander Popov #include <asm/setup.h> 255c9a8750SDmitry Vyukov 26ded97d2cSVictor Chibotaru /* Number of 64-bit words written per one comparison: */ 27ded97d2cSVictor Chibotaru #define KCOV_WORDS_PER_CMP 4 28ded97d2cSVictor Chibotaru 295c9a8750SDmitry Vyukov /* 305c9a8750SDmitry Vyukov * kcov descriptor (one per opened debugfs file). 315c9a8750SDmitry Vyukov * State transitions of the descriptor: 325c9a8750SDmitry Vyukov * - initial state after open() 335c9a8750SDmitry Vyukov * - then there must be a single ioctl(KCOV_INIT_TRACE) call 345c9a8750SDmitry Vyukov * - then, mmap() call (several calls are allowed but not useful) 35ded97d2cSVictor Chibotaru * - then, ioctl(KCOV_ENABLE, arg), where arg is 36ded97d2cSVictor Chibotaru * KCOV_TRACE_PC - to trace only the PCs 37ded97d2cSVictor Chibotaru * or 38ded97d2cSVictor Chibotaru * KCOV_TRACE_CMP - to trace only the comparison operands 39ded97d2cSVictor Chibotaru * - then, ioctl(KCOV_DISABLE) to disable the task. 40ded97d2cSVictor Chibotaru * Enabling/disabling ioctls can be repeated (only one task a time allowed). 415c9a8750SDmitry Vyukov */ 425c9a8750SDmitry Vyukov struct kcov { 435c9a8750SDmitry Vyukov /* 445c9a8750SDmitry Vyukov * Reference counter. We keep one for: 455c9a8750SDmitry Vyukov * - opened file descriptor 465c9a8750SDmitry Vyukov * - task with enabled coverage (we can't unwire it from another task) 475c9a8750SDmitry Vyukov */ 48*39e07cb6SElena Reshetova refcount_t refcount; 495c9a8750SDmitry Vyukov /* The lock protects mode, size, area and t. */ 505c9a8750SDmitry Vyukov spinlock_t lock; 515c9a8750SDmitry Vyukov enum kcov_mode mode; 525c9a8750SDmitry Vyukov /* Size of arena (in long's for KCOV_MODE_TRACE). */ 535c9a8750SDmitry Vyukov unsigned size; 545c9a8750SDmitry Vyukov /* Coverage buffer shared with user space. */ 555c9a8750SDmitry Vyukov void *area; 565c9a8750SDmitry Vyukov /* Task for which we collect coverage, or NULL. */ 575c9a8750SDmitry Vyukov struct task_struct *t; 585c9a8750SDmitry Vyukov }; 595c9a8750SDmitry Vyukov 60903e8ff8SAnders Roxell static notrace bool check_kcov_mode(enum kcov_mode needed_mode, struct task_struct *t) 615c9a8750SDmitry Vyukov { 620ed557aaSMark Rutland unsigned int mode; 635c9a8750SDmitry Vyukov 645c9a8750SDmitry Vyukov /* 655c9a8750SDmitry Vyukov * We are interested in code coverage as a function of a syscall inputs, 665c9a8750SDmitry Vyukov * so we ignore code executed in interrupts. 675c9a8750SDmitry Vyukov */ 68fcf4edacSAndrey Ryabinin if (!in_task()) 69ded97d2cSVictor Chibotaru return false; 705c9a8750SDmitry Vyukov mode = READ_ONCE(t->kcov_mode); 715c9a8750SDmitry Vyukov /* 725c9a8750SDmitry Vyukov * There is some code that runs in interrupts but for which 735c9a8750SDmitry Vyukov * in_interrupt() returns false (e.g. preempt_schedule_irq()). 745c9a8750SDmitry Vyukov * READ_ONCE()/barrier() effectively provides load-acquire wrt 755c9a8750SDmitry Vyukov * interrupts, there are paired barrier()/WRITE_ONCE() in 765c9a8750SDmitry Vyukov * kcov_ioctl_locked(). 775c9a8750SDmitry Vyukov */ 785c9a8750SDmitry Vyukov barrier(); 79ded97d2cSVictor Chibotaru return mode == needed_mode; 80ded97d2cSVictor Chibotaru } 81ded97d2cSVictor Chibotaru 82903e8ff8SAnders Roxell static notrace unsigned long canonicalize_ip(unsigned long ip) 83ded97d2cSVictor Chibotaru { 84ded97d2cSVictor Chibotaru #ifdef CONFIG_RANDOMIZE_BASE 85ded97d2cSVictor Chibotaru ip -= kaslr_offset(); 86ded97d2cSVictor Chibotaru #endif 87ded97d2cSVictor Chibotaru return ip; 88ded97d2cSVictor Chibotaru } 89ded97d2cSVictor Chibotaru 90ded97d2cSVictor Chibotaru /* 91ded97d2cSVictor Chibotaru * Entry point from instrumented code. 92ded97d2cSVictor Chibotaru * This is called once per basic-block/edge. 93ded97d2cSVictor Chibotaru */ 94ded97d2cSVictor Chibotaru void notrace __sanitizer_cov_trace_pc(void) 95ded97d2cSVictor Chibotaru { 96ded97d2cSVictor Chibotaru struct task_struct *t; 97ded97d2cSVictor Chibotaru unsigned long *area; 98ded97d2cSVictor Chibotaru unsigned long ip = canonicalize_ip(_RET_IP_); 99ded97d2cSVictor Chibotaru unsigned long pos; 100ded97d2cSVictor Chibotaru 101ded97d2cSVictor Chibotaru t = current; 102ded97d2cSVictor Chibotaru if (!check_kcov_mode(KCOV_MODE_TRACE_PC, t)) 103ded97d2cSVictor Chibotaru return; 104ded97d2cSVictor Chibotaru 1055c9a8750SDmitry Vyukov area = t->kcov_area; 106ded97d2cSVictor Chibotaru /* The first 64-bit word is the number of subsequent PCs. */ 1075c9a8750SDmitry Vyukov pos = READ_ONCE(area[0]) + 1; 1085c9a8750SDmitry Vyukov if (likely(pos < t->kcov_size)) { 1094983f0abSAlexander Popov area[pos] = ip; 1105c9a8750SDmitry Vyukov WRITE_ONCE(area[0], pos); 1115c9a8750SDmitry Vyukov } 1125c9a8750SDmitry Vyukov } 1135c9a8750SDmitry Vyukov EXPORT_SYMBOL(__sanitizer_cov_trace_pc); 1145c9a8750SDmitry Vyukov 115ded97d2cSVictor Chibotaru #ifdef CONFIG_KCOV_ENABLE_COMPARISONS 11663472443SAnders Roxell static void notrace write_comp_data(u64 type, u64 arg1, u64 arg2, u64 ip) 117ded97d2cSVictor Chibotaru { 118ded97d2cSVictor Chibotaru struct task_struct *t; 119ded97d2cSVictor Chibotaru u64 *area; 120ded97d2cSVictor Chibotaru u64 count, start_index, end_pos, max_pos; 121ded97d2cSVictor Chibotaru 122ded97d2cSVictor Chibotaru t = current; 123ded97d2cSVictor Chibotaru if (!check_kcov_mode(KCOV_MODE_TRACE_CMP, t)) 124ded97d2cSVictor Chibotaru return; 125ded97d2cSVictor Chibotaru 126ded97d2cSVictor Chibotaru ip = canonicalize_ip(ip); 127ded97d2cSVictor Chibotaru 128ded97d2cSVictor Chibotaru /* 129ded97d2cSVictor Chibotaru * We write all comparison arguments and types as u64. 130ded97d2cSVictor Chibotaru * The buffer was allocated for t->kcov_size unsigned longs. 131ded97d2cSVictor Chibotaru */ 132ded97d2cSVictor Chibotaru area = (u64 *)t->kcov_area; 133ded97d2cSVictor Chibotaru max_pos = t->kcov_size * sizeof(unsigned long); 134ded97d2cSVictor Chibotaru 135ded97d2cSVictor Chibotaru count = READ_ONCE(area[0]); 136ded97d2cSVictor Chibotaru 137ded97d2cSVictor Chibotaru /* Every record is KCOV_WORDS_PER_CMP 64-bit words. */ 138ded97d2cSVictor Chibotaru start_index = 1 + count * KCOV_WORDS_PER_CMP; 139ded97d2cSVictor Chibotaru end_pos = (start_index + KCOV_WORDS_PER_CMP) * sizeof(u64); 140ded97d2cSVictor Chibotaru if (likely(end_pos <= max_pos)) { 141ded97d2cSVictor Chibotaru area[start_index] = type; 142ded97d2cSVictor Chibotaru area[start_index + 1] = arg1; 143ded97d2cSVictor Chibotaru area[start_index + 2] = arg2; 144ded97d2cSVictor Chibotaru area[start_index + 3] = ip; 145ded97d2cSVictor Chibotaru WRITE_ONCE(area[0], count + 1); 146ded97d2cSVictor Chibotaru } 147ded97d2cSVictor Chibotaru } 148ded97d2cSVictor Chibotaru 149ded97d2cSVictor Chibotaru void notrace __sanitizer_cov_trace_cmp1(u8 arg1, u8 arg2) 150ded97d2cSVictor Chibotaru { 151ded97d2cSVictor Chibotaru write_comp_data(KCOV_CMP_SIZE(0), arg1, arg2, _RET_IP_); 152ded97d2cSVictor Chibotaru } 153ded97d2cSVictor Chibotaru EXPORT_SYMBOL(__sanitizer_cov_trace_cmp1); 154ded97d2cSVictor Chibotaru 155ded97d2cSVictor Chibotaru void notrace __sanitizer_cov_trace_cmp2(u16 arg1, u16 arg2) 156ded97d2cSVictor Chibotaru { 157ded97d2cSVictor Chibotaru write_comp_data(KCOV_CMP_SIZE(1), arg1, arg2, _RET_IP_); 158ded97d2cSVictor Chibotaru } 159ded97d2cSVictor Chibotaru EXPORT_SYMBOL(__sanitizer_cov_trace_cmp2); 160ded97d2cSVictor Chibotaru 161689d77f0SDmitry Vyukov void notrace __sanitizer_cov_trace_cmp4(u32 arg1, u32 arg2) 162ded97d2cSVictor Chibotaru { 163ded97d2cSVictor Chibotaru write_comp_data(KCOV_CMP_SIZE(2), arg1, arg2, _RET_IP_); 164ded97d2cSVictor Chibotaru } 165ded97d2cSVictor Chibotaru EXPORT_SYMBOL(__sanitizer_cov_trace_cmp4); 166ded97d2cSVictor Chibotaru 167ded97d2cSVictor Chibotaru void notrace __sanitizer_cov_trace_cmp8(u64 arg1, u64 arg2) 168ded97d2cSVictor Chibotaru { 169ded97d2cSVictor Chibotaru write_comp_data(KCOV_CMP_SIZE(3), arg1, arg2, _RET_IP_); 170ded97d2cSVictor Chibotaru } 171ded97d2cSVictor Chibotaru EXPORT_SYMBOL(__sanitizer_cov_trace_cmp8); 172ded97d2cSVictor Chibotaru 173ded97d2cSVictor Chibotaru void notrace __sanitizer_cov_trace_const_cmp1(u8 arg1, u8 arg2) 174ded97d2cSVictor Chibotaru { 175ded97d2cSVictor Chibotaru write_comp_data(KCOV_CMP_SIZE(0) | KCOV_CMP_CONST, arg1, arg2, 176ded97d2cSVictor Chibotaru _RET_IP_); 177ded97d2cSVictor Chibotaru } 178ded97d2cSVictor Chibotaru EXPORT_SYMBOL(__sanitizer_cov_trace_const_cmp1); 179ded97d2cSVictor Chibotaru 180ded97d2cSVictor Chibotaru void notrace __sanitizer_cov_trace_const_cmp2(u16 arg1, u16 arg2) 181ded97d2cSVictor Chibotaru { 182ded97d2cSVictor Chibotaru write_comp_data(KCOV_CMP_SIZE(1) | KCOV_CMP_CONST, arg1, arg2, 183ded97d2cSVictor Chibotaru _RET_IP_); 184ded97d2cSVictor Chibotaru } 185ded97d2cSVictor Chibotaru EXPORT_SYMBOL(__sanitizer_cov_trace_const_cmp2); 186ded97d2cSVictor Chibotaru 187689d77f0SDmitry Vyukov void notrace __sanitizer_cov_trace_const_cmp4(u32 arg1, u32 arg2) 188ded97d2cSVictor Chibotaru { 189ded97d2cSVictor Chibotaru write_comp_data(KCOV_CMP_SIZE(2) | KCOV_CMP_CONST, arg1, arg2, 190ded97d2cSVictor Chibotaru _RET_IP_); 191ded97d2cSVictor Chibotaru } 192ded97d2cSVictor Chibotaru EXPORT_SYMBOL(__sanitizer_cov_trace_const_cmp4); 193ded97d2cSVictor Chibotaru 194ded97d2cSVictor Chibotaru void notrace __sanitizer_cov_trace_const_cmp8(u64 arg1, u64 arg2) 195ded97d2cSVictor Chibotaru { 196ded97d2cSVictor Chibotaru write_comp_data(KCOV_CMP_SIZE(3) | KCOV_CMP_CONST, arg1, arg2, 197ded97d2cSVictor Chibotaru _RET_IP_); 198ded97d2cSVictor Chibotaru } 199ded97d2cSVictor Chibotaru EXPORT_SYMBOL(__sanitizer_cov_trace_const_cmp8); 200ded97d2cSVictor Chibotaru 201ded97d2cSVictor Chibotaru void notrace __sanitizer_cov_trace_switch(u64 val, u64 *cases) 202ded97d2cSVictor Chibotaru { 203ded97d2cSVictor Chibotaru u64 i; 204ded97d2cSVictor Chibotaru u64 count = cases[0]; 205ded97d2cSVictor Chibotaru u64 size = cases[1]; 206ded97d2cSVictor Chibotaru u64 type = KCOV_CMP_CONST; 207ded97d2cSVictor Chibotaru 208ded97d2cSVictor Chibotaru switch (size) { 209ded97d2cSVictor Chibotaru case 8: 210ded97d2cSVictor Chibotaru type |= KCOV_CMP_SIZE(0); 211ded97d2cSVictor Chibotaru break; 212ded97d2cSVictor Chibotaru case 16: 213ded97d2cSVictor Chibotaru type |= KCOV_CMP_SIZE(1); 214ded97d2cSVictor Chibotaru break; 215ded97d2cSVictor Chibotaru case 32: 216ded97d2cSVictor Chibotaru type |= KCOV_CMP_SIZE(2); 217ded97d2cSVictor Chibotaru break; 218ded97d2cSVictor Chibotaru case 64: 219ded97d2cSVictor Chibotaru type |= KCOV_CMP_SIZE(3); 220ded97d2cSVictor Chibotaru break; 221ded97d2cSVictor Chibotaru default: 222ded97d2cSVictor Chibotaru return; 223ded97d2cSVictor Chibotaru } 224ded97d2cSVictor Chibotaru for (i = 0; i < count; i++) 225ded97d2cSVictor Chibotaru write_comp_data(type, cases[i + 2], val, _RET_IP_); 226ded97d2cSVictor Chibotaru } 227ded97d2cSVictor Chibotaru EXPORT_SYMBOL(__sanitizer_cov_trace_switch); 228ded97d2cSVictor Chibotaru #endif /* ifdef CONFIG_KCOV_ENABLE_COMPARISONS */ 229ded97d2cSVictor Chibotaru 2305c9a8750SDmitry Vyukov static void kcov_get(struct kcov *kcov) 2315c9a8750SDmitry Vyukov { 232*39e07cb6SElena Reshetova refcount_inc(&kcov->refcount); 2335c9a8750SDmitry Vyukov } 2345c9a8750SDmitry Vyukov 2355c9a8750SDmitry Vyukov static void kcov_put(struct kcov *kcov) 2365c9a8750SDmitry Vyukov { 237*39e07cb6SElena Reshetova if (refcount_dec_and_test(&kcov->refcount)) { 2385c9a8750SDmitry Vyukov vfree(kcov->area); 2395c9a8750SDmitry Vyukov kfree(kcov); 2405c9a8750SDmitry Vyukov } 2415c9a8750SDmitry Vyukov } 2425c9a8750SDmitry Vyukov 2435c9a8750SDmitry Vyukov void kcov_task_init(struct task_struct *t) 2445c9a8750SDmitry Vyukov { 245c9484b98SMark Rutland WRITE_ONCE(t->kcov_mode, KCOV_MODE_DISABLED); 246c9484b98SMark Rutland barrier(); 2475c9a8750SDmitry Vyukov t->kcov_size = 0; 2485c9a8750SDmitry Vyukov t->kcov_area = NULL; 2495c9a8750SDmitry Vyukov t->kcov = NULL; 2505c9a8750SDmitry Vyukov } 2515c9a8750SDmitry Vyukov 2525c9a8750SDmitry Vyukov void kcov_task_exit(struct task_struct *t) 2535c9a8750SDmitry Vyukov { 2545c9a8750SDmitry Vyukov struct kcov *kcov; 2555c9a8750SDmitry Vyukov 2565c9a8750SDmitry Vyukov kcov = t->kcov; 2575c9a8750SDmitry Vyukov if (kcov == NULL) 2585c9a8750SDmitry Vyukov return; 2595c9a8750SDmitry Vyukov spin_lock(&kcov->lock); 2605c9a8750SDmitry Vyukov if (WARN_ON(kcov->t != t)) { 2615c9a8750SDmitry Vyukov spin_unlock(&kcov->lock); 2625c9a8750SDmitry Vyukov return; 2635c9a8750SDmitry Vyukov } 2645c9a8750SDmitry Vyukov /* Just to not leave dangling references behind. */ 2655c9a8750SDmitry Vyukov kcov_task_init(t); 2665c9a8750SDmitry Vyukov kcov->t = NULL; 267ded97d2cSVictor Chibotaru kcov->mode = KCOV_MODE_INIT; 2685c9a8750SDmitry Vyukov spin_unlock(&kcov->lock); 2695c9a8750SDmitry Vyukov kcov_put(kcov); 2705c9a8750SDmitry Vyukov } 2715c9a8750SDmitry Vyukov 2725c9a8750SDmitry Vyukov static int kcov_mmap(struct file *filep, struct vm_area_struct *vma) 2735c9a8750SDmitry Vyukov { 2745c9a8750SDmitry Vyukov int res = 0; 2755c9a8750SDmitry Vyukov void *area; 2765c9a8750SDmitry Vyukov struct kcov *kcov = vma->vm_file->private_data; 2775c9a8750SDmitry Vyukov unsigned long size, off; 2785c9a8750SDmitry Vyukov struct page *page; 2795c9a8750SDmitry Vyukov 2805c9a8750SDmitry Vyukov area = vmalloc_user(vma->vm_end - vma->vm_start); 2815c9a8750SDmitry Vyukov if (!area) 2825c9a8750SDmitry Vyukov return -ENOMEM; 2835c9a8750SDmitry Vyukov 2845c9a8750SDmitry Vyukov spin_lock(&kcov->lock); 2855c9a8750SDmitry Vyukov size = kcov->size * sizeof(unsigned long); 286ded97d2cSVictor Chibotaru if (kcov->mode != KCOV_MODE_INIT || vma->vm_pgoff != 0 || 2875c9a8750SDmitry Vyukov vma->vm_end - vma->vm_start != size) { 2885c9a8750SDmitry Vyukov res = -EINVAL; 2895c9a8750SDmitry Vyukov goto exit; 2905c9a8750SDmitry Vyukov } 2915c9a8750SDmitry Vyukov if (!kcov->area) { 2925c9a8750SDmitry Vyukov kcov->area = area; 2935c9a8750SDmitry Vyukov vma->vm_flags |= VM_DONTEXPAND; 2945c9a8750SDmitry Vyukov spin_unlock(&kcov->lock); 2955c9a8750SDmitry Vyukov for (off = 0; off < size; off += PAGE_SIZE) { 2965c9a8750SDmitry Vyukov page = vmalloc_to_page(kcov->area + off); 2975c9a8750SDmitry Vyukov if (vm_insert_page(vma, vma->vm_start + off, page)) 2985c9a8750SDmitry Vyukov WARN_ONCE(1, "vm_insert_page() failed"); 2995c9a8750SDmitry Vyukov } 3005c9a8750SDmitry Vyukov return 0; 3015c9a8750SDmitry Vyukov } 3025c9a8750SDmitry Vyukov exit: 3035c9a8750SDmitry Vyukov spin_unlock(&kcov->lock); 3045c9a8750SDmitry Vyukov vfree(area); 3055c9a8750SDmitry Vyukov return res; 3065c9a8750SDmitry Vyukov } 3075c9a8750SDmitry Vyukov 3085c9a8750SDmitry Vyukov static int kcov_open(struct inode *inode, struct file *filep) 3095c9a8750SDmitry Vyukov { 3105c9a8750SDmitry Vyukov struct kcov *kcov; 3115c9a8750SDmitry Vyukov 3125c9a8750SDmitry Vyukov kcov = kzalloc(sizeof(*kcov), GFP_KERNEL); 3135c9a8750SDmitry Vyukov if (!kcov) 3145c9a8750SDmitry Vyukov return -ENOMEM; 315ded97d2cSVictor Chibotaru kcov->mode = KCOV_MODE_DISABLED; 316*39e07cb6SElena Reshetova refcount_set(&kcov->refcount, 1); 3175c9a8750SDmitry Vyukov spin_lock_init(&kcov->lock); 3185c9a8750SDmitry Vyukov filep->private_data = kcov; 3195c9a8750SDmitry Vyukov return nonseekable_open(inode, filep); 3205c9a8750SDmitry Vyukov } 3215c9a8750SDmitry Vyukov 3225c9a8750SDmitry Vyukov static int kcov_close(struct inode *inode, struct file *filep) 3235c9a8750SDmitry Vyukov { 3245c9a8750SDmitry Vyukov kcov_put(filep->private_data); 3255c9a8750SDmitry Vyukov return 0; 3265c9a8750SDmitry Vyukov } 3275c9a8750SDmitry Vyukov 328dc55daffSMark Rutland /* 329dc55daffSMark Rutland * Fault in a lazily-faulted vmalloc area before it can be used by 330dc55daffSMark Rutland * __santizer_cov_trace_pc(), to avoid recursion issues if any code on the 331dc55daffSMark Rutland * vmalloc fault handling path is instrumented. 332dc55daffSMark Rutland */ 333dc55daffSMark Rutland static void kcov_fault_in_area(struct kcov *kcov) 334dc55daffSMark Rutland { 335dc55daffSMark Rutland unsigned long stride = PAGE_SIZE / sizeof(unsigned long); 336dc55daffSMark Rutland unsigned long *area = kcov->area; 337dc55daffSMark Rutland unsigned long offset; 338dc55daffSMark Rutland 339dc55daffSMark Rutland for (offset = 0; offset < kcov->size; offset += stride) 340dc55daffSMark Rutland READ_ONCE(area[offset]); 341dc55daffSMark Rutland } 342dc55daffSMark Rutland 3435c9a8750SDmitry Vyukov static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, 3445c9a8750SDmitry Vyukov unsigned long arg) 3455c9a8750SDmitry Vyukov { 3465c9a8750SDmitry Vyukov struct task_struct *t; 3475c9a8750SDmitry Vyukov unsigned long size, unused; 3485c9a8750SDmitry Vyukov 3495c9a8750SDmitry Vyukov switch (cmd) { 3505c9a8750SDmitry Vyukov case KCOV_INIT_TRACE: 3515c9a8750SDmitry Vyukov /* 3525c9a8750SDmitry Vyukov * Enable kcov in trace mode and setup buffer size. 3535c9a8750SDmitry Vyukov * Must happen before anything else. 3545c9a8750SDmitry Vyukov */ 3555c9a8750SDmitry Vyukov if (kcov->mode != KCOV_MODE_DISABLED) 3565c9a8750SDmitry Vyukov return -EBUSY; 3575c9a8750SDmitry Vyukov /* 3585c9a8750SDmitry Vyukov * Size must be at least 2 to hold current position and one PC. 3595c9a8750SDmitry Vyukov * Later we allocate size * sizeof(unsigned long) memory, 3605c9a8750SDmitry Vyukov * that must not overflow. 3615c9a8750SDmitry Vyukov */ 3625c9a8750SDmitry Vyukov size = arg; 3635c9a8750SDmitry Vyukov if (size < 2 || size > INT_MAX / sizeof(unsigned long)) 3645c9a8750SDmitry Vyukov return -EINVAL; 3655c9a8750SDmitry Vyukov kcov->size = size; 366ded97d2cSVictor Chibotaru kcov->mode = KCOV_MODE_INIT; 3675c9a8750SDmitry Vyukov return 0; 3685c9a8750SDmitry Vyukov case KCOV_ENABLE: 3695c9a8750SDmitry Vyukov /* 3705c9a8750SDmitry Vyukov * Enable coverage for the current task. 3715c9a8750SDmitry Vyukov * At this point user must have been enabled trace mode, 3725c9a8750SDmitry Vyukov * and mmapped the file. Coverage collection is disabled only 3735c9a8750SDmitry Vyukov * at task exit or voluntary by KCOV_DISABLE. After that it can 3745c9a8750SDmitry Vyukov * be enabled for another task. 3755c9a8750SDmitry Vyukov */ 376ded97d2cSVictor Chibotaru if (kcov->mode != KCOV_MODE_INIT || !kcov->area) 3775c9a8750SDmitry Vyukov return -EINVAL; 378a77660d2SDmitry Vyukov t = current; 379a77660d2SDmitry Vyukov if (kcov->t != NULL || t->kcov != NULL) 3805c9a8750SDmitry Vyukov return -EBUSY; 381ded97d2cSVictor Chibotaru if (arg == KCOV_TRACE_PC) 382ded97d2cSVictor Chibotaru kcov->mode = KCOV_MODE_TRACE_PC; 383ded97d2cSVictor Chibotaru else if (arg == KCOV_TRACE_CMP) 384ded97d2cSVictor Chibotaru #ifdef CONFIG_KCOV_ENABLE_COMPARISONS 385ded97d2cSVictor Chibotaru kcov->mode = KCOV_MODE_TRACE_CMP; 386ded97d2cSVictor Chibotaru #else 387ded97d2cSVictor Chibotaru return -ENOTSUPP; 388ded97d2cSVictor Chibotaru #endif 389ded97d2cSVictor Chibotaru else 390ded97d2cSVictor Chibotaru return -EINVAL; 391dc55daffSMark Rutland kcov_fault_in_area(kcov); 3925c9a8750SDmitry Vyukov /* Cache in task struct for performance. */ 3935c9a8750SDmitry Vyukov t->kcov_size = kcov->size; 3945c9a8750SDmitry Vyukov t->kcov_area = kcov->area; 395ded97d2cSVictor Chibotaru /* See comment in check_kcov_mode(). */ 3965c9a8750SDmitry Vyukov barrier(); 3975c9a8750SDmitry Vyukov WRITE_ONCE(t->kcov_mode, kcov->mode); 3985c9a8750SDmitry Vyukov t->kcov = kcov; 3995c9a8750SDmitry Vyukov kcov->t = t; 4005c9a8750SDmitry Vyukov /* This is put either in kcov_task_exit() or in KCOV_DISABLE. */ 4015c9a8750SDmitry Vyukov kcov_get(kcov); 4025c9a8750SDmitry Vyukov return 0; 4035c9a8750SDmitry Vyukov case KCOV_DISABLE: 4045c9a8750SDmitry Vyukov /* Disable coverage for the current task. */ 4055c9a8750SDmitry Vyukov unused = arg; 4065c9a8750SDmitry Vyukov if (unused != 0 || current->kcov != kcov) 4075c9a8750SDmitry Vyukov return -EINVAL; 4085c9a8750SDmitry Vyukov t = current; 4095c9a8750SDmitry Vyukov if (WARN_ON(kcov->t != t)) 4105c9a8750SDmitry Vyukov return -EINVAL; 4115c9a8750SDmitry Vyukov kcov_task_init(t); 4125c9a8750SDmitry Vyukov kcov->t = NULL; 413ded97d2cSVictor Chibotaru kcov->mode = KCOV_MODE_INIT; 4145c9a8750SDmitry Vyukov kcov_put(kcov); 4155c9a8750SDmitry Vyukov return 0; 4165c9a8750SDmitry Vyukov default: 4175c9a8750SDmitry Vyukov return -ENOTTY; 4185c9a8750SDmitry Vyukov } 4195c9a8750SDmitry Vyukov } 4205c9a8750SDmitry Vyukov 4215c9a8750SDmitry Vyukov static long kcov_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) 4225c9a8750SDmitry Vyukov { 4235c9a8750SDmitry Vyukov struct kcov *kcov; 4245c9a8750SDmitry Vyukov int res; 4255c9a8750SDmitry Vyukov 4265c9a8750SDmitry Vyukov kcov = filep->private_data; 4275c9a8750SDmitry Vyukov spin_lock(&kcov->lock); 4285c9a8750SDmitry Vyukov res = kcov_ioctl_locked(kcov, cmd, arg); 4295c9a8750SDmitry Vyukov spin_unlock(&kcov->lock); 4305c9a8750SDmitry Vyukov return res; 4315c9a8750SDmitry Vyukov } 4325c9a8750SDmitry Vyukov 4335c9a8750SDmitry Vyukov static const struct file_operations kcov_fops = { 4345c9a8750SDmitry Vyukov .open = kcov_open, 4355c9a8750SDmitry Vyukov .unlocked_ioctl = kcov_ioctl, 4367483e5d4SDmitry Vyukov .compat_ioctl = kcov_ioctl, 4375c9a8750SDmitry Vyukov .mmap = kcov_mmap, 4385c9a8750SDmitry Vyukov .release = kcov_close, 4395c9a8750SDmitry Vyukov }; 4405c9a8750SDmitry Vyukov 4415c9a8750SDmitry Vyukov static int __init kcov_init(void) 4425c9a8750SDmitry Vyukov { 443df4565f9SNicolai Stange /* 444df4565f9SNicolai Stange * The kcov debugfs file won't ever get removed and thus, 445df4565f9SNicolai Stange * there is no need to protect it against removal races. The 446df4565f9SNicolai Stange * use of debugfs_create_file_unsafe() is actually safe here. 447df4565f9SNicolai Stange */ 448ec9672d5SGreg Kroah-Hartman debugfs_create_file_unsafe("kcov", 0600, NULL, NULL, &kcov_fops); 449ec9672d5SGreg Kroah-Hartman 4505c9a8750SDmitry Vyukov return 0; 4515c9a8750SDmitry Vyukov } 4525c9a8750SDmitry Vyukov 4535c9a8750SDmitry Vyukov device_initcall(kcov_init); 454