aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux/instrumentation.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/linux/instrumentation.h')
-rw-r--r--include/linux/instrumentation.h61
1 files changed, 61 insertions, 0 deletions
diff --git a/include/linux/instrumentation.h b/include/linux/instrumentation.h
new file mode 100644
index 000000000000..bc7babe91b2e
--- /dev/null
+++ b/include/linux/instrumentation.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_INSTRUMENTATION_H
+#define __LINUX_INSTRUMENTATION_H
+
+#ifdef CONFIG_NOINSTR_VALIDATION
+
+#include <linux/stringify.h>
+
+/* Begin/end of an instrumentation safe region */
+#define __instrumentation_begin(c) ({ \
+ asm volatile(__stringify(c) ": nop\n\t" \
+ ".pushsection .discard.instr_begin\n\t" \
+ ".long " __stringify(c) "b - .\n\t" \
+ ".popsection\n\t" : : "i" (c)); \
+})
+#define instrumentation_begin() __instrumentation_begin(__COUNTER__)
+
+/*
+ * Because instrumentation_{begin,end}() can nest, objtool validation considers
+ * _begin() a +1 and _end() a -1 and computes a sum over the instructions.
+ * When the value is greater than 0, we consider instrumentation allowed.
+ *
+ * There is a problem with code like:
+ *
+ * noinstr void foo()
+ * {
+ * instrumentation_begin();
+ * ...
+ * if (cond) {
+ * instrumentation_begin();
+ * ...
+ * instrumentation_end();
+ * }
+ * bar();
+ * instrumentation_end();
+ * }
+ *
+ * If instrumentation_end() would be an empty label, like all the other
+ * annotations, the inner _end(), which is at the end of a conditional block,
+ * would land on the instruction after the block.
+ *
+ * If we then consider the sum of the !cond path, we'll see that the call to
+ * bar() is with a 0-value, even though, we meant it to happen with a positive
+ * value.
+ *
+ * To avoid this, have _end() be a NOP instruction, this ensures it will be
+ * part of the condition block and does not escape.
+ */
+#define __instrumentation_end(c) ({ \
+ asm volatile(__stringify(c) ": nop\n\t" \
+ ".pushsection .discard.instr_end\n\t" \
+ ".long " __stringify(c) "b - .\n\t" \
+ ".popsection\n\t" : : "i" (c)); \
+})
+#define instrumentation_end() __instrumentation_end(__COUNTER__)
+#else /* !CONFIG_NOINSTR_VALIDATION */
+# define instrumentation_begin() do { } while(0)
+# define instrumentation_end() do { } while(0)
+#endif /* CONFIG_NOINSTR_VALIDATION */
+
+#endif /* __LINUX_INSTRUMENTATION_H */