*/
 
 #include <subcmd/parse-options.h>
+#include <string.h>
 #include "builtin.h"
 #include "check.h"
 
-bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
+bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
 
 static const char * const check_usage[] = {
        "objtool check [<options>] file.o",
        OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
        OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"),
        OPT_BOOLEAN('s', "stats", &stats, "print statistics"),
+       OPT_BOOLEAN('d', "duplicate", &validate_dup, "duplicate validation for vmlinux.o"),
+       OPT_BOOLEAN('l', "vmlinux", &vmlinux, "vmlinux.o validation"),
        OPT_END(),
 };
 
 int cmd_check(int argc, const char **argv)
 {
-       const char *objname;
+       const char *objname, *s;
 
        argc = parse_options(argc, argv, check_options, check_usage, 0);
 
 
        objname = argv[0];
 
+       s = strstr(objname, "vmlinux.o");
+       if (s && !s[9])
+               vmlinux = true;
+
        return check(objname, false);
 }
 
 #include <subcmd/parse-options.h>
 
 extern const struct option check_options[];
-extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
+extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
 
 extern int cmd_check(int argc, const char **argv);
 extern int cmd_orc(int argc, const char **argv);
 
                    strncmp(sec->name, ".discard.", 9))
                        sec->text = true;
 
+               if (!strcmp(sec->name, ".noinstr.text"))
+                       sec->noinstr = true;
+
                for (offset = 0; offset < sec->len; offset += insn->len) {
                        insn = malloc(sizeof(*insn));
                        if (!insn) {
        return 0;
 }
 
+static int read_instr_hints(struct objtool_file *file)
+{
+       struct section *sec;
+       struct instruction *insn;
+       struct rela *rela;
+
+       sec = find_section_by_name(file->elf, ".rela.discard.instr_end");
+       if (!sec)
+               return 0;
+
+       list_for_each_entry(rela, &sec->rela_list, list) {
+               if (rela->sym->type != STT_SECTION) {
+                       WARN("unexpected relocation symbol type in %s", sec->name);
+                       return -1;
+               }
+
+               insn = find_insn(file, rela->sym->sec, rela->addend);
+               if (!insn) {
+                       WARN("bad .discard.instr_end entry");
+                       return -1;
+               }
+
+               insn->instr--;
+       }
+
+       sec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
+       if (!sec)
+               return 0;
+
+       list_for_each_entry(rela, &sec->rela_list, list) {
+               if (rela->sym->type != STT_SECTION) {
+                       WARN("unexpected relocation symbol type in %s", sec->name);
+                       return -1;
+               }
+
+               insn = find_insn(file, rela->sym->sec, rela->addend);
+               if (!insn) {
+                       WARN("bad .discard.instr_begin entry");
+                       return -1;
+               }
+
+               insn->instr++;
+       }
+
+       return 0;
+}
+
 static void mark_rodata(struct objtool_file *file)
 {
        struct section *sec;
        if (ret)
                return ret;
 
+       ret = read_instr_hints(file);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
 
 static int validate_call(struct instruction *insn, struct insn_state *state)
 {
+       if (state->noinstr && state->instr <= 0 &&
+           (!insn->call_dest || insn->call_dest->sec != insn->sec)) {
+               WARN_FUNC("call to %s() leaves .noinstr.text section",
+                               insn->sec, insn->offset, call_dest_name(insn));
+               return 1;
+       }
+
        if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
                WARN_FUNC("call to %s() with UACCESS enabled",
                                insn->sec, insn->offset, call_dest_name(insn));
 
 static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
 {
+       if (state->noinstr && state->instr > 0) {
+               WARN_FUNC("return with instrumentation enabled",
+                         insn->sec, insn->offset);
+               return 1;
+       }
+
        if (state->uaccess && !func_uaccess_safe(func)) {
                WARN_FUNC("return with UACCESS enabled",
                          insn->sec, insn->offset);
                                return 0;
                }
 
+               if (state.noinstr)
+                       state.instr += insn->instr;
+
                if (insn->hint)
                        state.cfi = insn->cfi;
                else
        struct insn_state state;
        int ret, warnings = 0;
 
+       /*
+        * We need the full vmlinux for noinstr validation, otherwise we can
+        * not correctly determine insn->call_dest->sec (external symbols do
+        * not have a section).
+        */
+       if (vmlinux)
+               state.noinstr = sec->noinstr;
+
        list_for_each_entry(func, &sec->symbol_list, list) {
                if (func->type != STT_FUNC)
                        continue;
        return warnings;
 }
 
+static int validate_vmlinux_functions(struct objtool_file *file)
+{
+       struct section *sec;
+
+       sec = find_section_by_name(file->elf, ".noinstr.text");
+       if (!sec)
+               return 0;
+
+       return validate_section(file, sec);
+}
+
 static int validate_functions(struct objtool_file *file)
 {
        struct section *sec;
        if (list_empty(&file.insn_list))
                goto out;
 
+       if (vmlinux && !validate_dup) {
+               ret = validate_vmlinux_functions(&file);
+               if (ret < 0)
+                       goto out;
+
+               warnings += ret;
+               goto out;
+       }
+
        if (retpoline) {
                ret = validate_retpoline(&file);
                if (ret < 0)