1 /* SPDX-License-Identifier: GPL-2.0 */ 2 #ifndef _LINUX_KCOV_H 3 #define _LINUX_KCOV_H 4 5 #include <linux/sched.h> 6 #include <uapi/linux/kcov.h> 7 8 struct task_struct; 9 10 #ifdef CONFIG_KCOV 11 12 enum kcov_mode { 13 /* Coverage collection is not enabled yet. */ 14 KCOV_MODE_DISABLED = 0, 15 /* KCOV was initialized, but tracing mode hasn't been chosen yet. */ 16 KCOV_MODE_INIT = 1, 17 /* 18 * Tracing coverage collection mode. 19 * Covered PCs are collected in a per-task buffer. 20 */ 21 KCOV_MODE_TRACE_PC = 2, 22 /* Collecting comparison operands mode. */ 23 KCOV_MODE_TRACE_CMP = 3, 24 }; 25 26 #define KCOV_IN_CTXSW (1 << 30) 27 28 void kcov_task_init(struct task_struct *t); 29 void kcov_task_exit(struct task_struct *t); 30 31 #define kcov_prepare_switch(t) \ 32 do { \ 33 (t)->kcov_mode |= KCOV_IN_CTXSW; \ 34 } while (0) 35 36 #define kcov_finish_switch(t) \ 37 do { \ 38 (t)->kcov_mode &= ~KCOV_IN_CTXSW; \ 39 } while (0) 40 41 /* See Documentation/dev-tools/kcov.rst for usage details. */ 42 void kcov_remote_start(u64 handle); 43 void kcov_remote_stop(void); 44 u64 kcov_common_handle(void); 45 46 static inline void kcov_remote_start_common(u64 id) 47 { 48 kcov_remote_start(kcov_remote_handle(KCOV_SUBSYSTEM_COMMON, id)); 49 } 50 51 static inline void kcov_remote_start_usb(u64 id) 52 { 53 kcov_remote_start(kcov_remote_handle(KCOV_SUBSYSTEM_USB, id)); 54 } 55 56 /* 57 * The softirq flavor of kcov_remote_*() functions is introduced as a temporary 58 * workaround for KCOV's lack of nested remote coverage sections support. 59 * 60 * Adding support is tracked in https://bugzilla.kernel.org/show_bug.cgi?id=210337. 61 * 62 * kcov_remote_start_usb_softirq(): 63 * 64 * 1. Only collects coverage when called in the softirq context. This allows 65 * avoiding nested remote coverage collection sections in the task context. 66 * For example, USB/IP calls usb_hcd_giveback_urb() in the task context 67 * within an existing remote coverage collection section. Thus, KCOV should 68 * not attempt to start collecting coverage within the coverage collection 69 * section in __usb_hcd_giveback_urb() in this case. 70 * 71 * 2. Disables interrupts for the duration of the coverage collection section. 72 * This allows avoiding nested remote coverage collection sections in the 73 * softirq context (a softirq might occur during the execution of a work in 74 * the BH workqueue, which runs with in_serving_softirq() > 0). 75 * For example, usb_giveback_urb_bh() runs in the BH workqueue with 76 * interrupts enabled, so __usb_hcd_giveback_urb() might be interrupted in 77 * the middle of its remote coverage collection section, and the interrupt 78 * handler might invoke __usb_hcd_giveback_urb() again. 79 */ 80 81 static inline unsigned long kcov_remote_start_usb_softirq(u64 id) 82 { 83 unsigned long flags = 0; 84 85 if (in_serving_softirq()) { 86 local_irq_save(flags); 87 kcov_remote_start_usb(id); 88 } 89 90 return flags; 91 } 92 93 static inline void kcov_remote_stop_softirq(unsigned long flags) 94 { 95 if (in_serving_softirq()) { 96 kcov_remote_stop(); 97 local_irq_restore(flags); 98 } 99 } 100 101 #ifdef CONFIG_64BIT 102 typedef unsigned long kcov_u64; 103 #else 104 typedef unsigned long long kcov_u64; 105 #endif 106 107 void __sanitizer_cov_trace_pc(void); 108 void __sanitizer_cov_trace_cmp1(u8 arg1, u8 arg2); 109 void __sanitizer_cov_trace_cmp2(u16 arg1, u16 arg2); 110 void __sanitizer_cov_trace_cmp4(u32 arg1, u32 arg2); 111 void __sanitizer_cov_trace_cmp8(kcov_u64 arg1, kcov_u64 arg2); 112 void __sanitizer_cov_trace_const_cmp1(u8 arg1, u8 arg2); 113 void __sanitizer_cov_trace_const_cmp2(u16 arg1, u16 arg2); 114 void __sanitizer_cov_trace_const_cmp4(u32 arg1, u32 arg2); 115 void __sanitizer_cov_trace_const_cmp8(kcov_u64 arg1, kcov_u64 arg2); 116 void __sanitizer_cov_trace_switch(kcov_u64 val, void *cases); 117 118 #else 119 120 static inline void kcov_task_init(struct task_struct *t) {} 121 static inline void kcov_task_exit(struct task_struct *t) {} 122 static inline void kcov_prepare_switch(struct task_struct *t) {} 123 static inline void kcov_finish_switch(struct task_struct *t) {} 124 static inline void kcov_remote_start(u64 handle) {} 125 static inline void kcov_remote_stop(void) {} 126 static inline u64 kcov_common_handle(void) 127 { 128 return 0; 129 } 130 static inline void kcov_remote_start_common(u64 id) {} 131 static inline void kcov_remote_start_usb(u64 id) {} 132 static inline unsigned long kcov_remote_start_usb_softirq(u64 id) 133 { 134 return 0; 135 } 136 static inline void kcov_remote_stop_softirq(unsigned long flags) {} 137 138 #endif /* CONFIG_KCOV */ 139 #endif /* _LINUX_KCOV_H */ 140