--- /dev/null
+/*
+ * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ORC_TYPES_H
+#define _ORC_TYPES_H
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+
+/*
+ * The ORC_REG_* registers are base registers which are used to find other
+ * registers on the stack.
+ *
+ * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the
+ * address of the previous frame: the caller's SP before it called the current
+ * function.
+ *
+ * ORC_REG_UNDEFINED means the corresponding register's value didn't change in
+ * the current frame.
+ *
+ * The most commonly used base registers are SP and BP -- which the previous SP
+ * is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is
+ * usually based on.
+ *
+ * The rest of the base registers are needed for special cases like entry code
+ * and GCC realigned stacks.
+ */
+#define ORC_REG_UNDEFINED              0
+#define ORC_REG_PREV_SP                        1
+#define ORC_REG_DX                     2
+#define ORC_REG_DI                     3
+#define ORC_REG_BP                     4
+#define ORC_REG_SP                     5
+#define ORC_REG_R10                    6
+#define ORC_REG_R13                    7
+#define ORC_REG_BP_INDIRECT            8
+#define ORC_REG_SP_INDIRECT            9
+#define ORC_REG_MAX                    15
+
+/*
+ * ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the
+ * caller's SP right before it made the call).  Used for all callable
+ * functions, i.e. all C code and all callable asm functions.
+ *
+ * ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points
+ * to a fully populated pt_regs from a syscall, interrupt, or exception.
+ *
+ * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
+ * points to the iret return frame.
+ *
+ * The UNWIND_HINT macros are used only for the unwind_hint struct.  They
+ * aren't used in struct orc_entry due to size and complexity constraints.
+ * Objtool converts them to real types when it converts the hints to orc
+ * entries.
+ */
+#define ORC_TYPE_CALL                  0
+#define ORC_TYPE_REGS                  1
+#define ORC_TYPE_REGS_IRET             2
+#define UNWIND_HINT_TYPE_SAVE          3
+#define UNWIND_HINT_TYPE_RESTORE       4
+
+#ifndef __ASSEMBLY__
+/*
+ * This struct is more or less a vastly simplified version of the DWARF Call
+ * Frame Information standard.  It contains only the necessary parts of DWARF
+ * CFI, simplified for ease of access by the in-kernel unwinder.  It tells the
+ * unwinder how to find the previous SP and BP (and sometimes entry regs) on
+ * the stack for a given code address.  Each instance of the struct corresponds
+ * to one or more code locations.
+ */
+struct orc_entry {
+       s16             sp_offset;
+       s16             bp_offset;
+       unsigned        sp_reg:4;
+       unsigned        bp_reg:4;
+       unsigned        type:2;
+};
+
+/*
+ * This struct is used by asm and inline asm code to manually annotate the
+ * location of registers on the stack for the ORC unwinder.
+ *
+ * Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*.
+ */
+struct unwind_hint {
+       u32             ip;
+       s16             sp_offset;
+       u8              sp_reg;
+       u8              type;
+};
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ORC_TYPES_H */
 
--- /dev/null
+#ifndef _ASM_X86_UNWIND_HINTS_H
+#define _ASM_X86_UNWIND_HINTS_H
+
+#include "orc_types.h"
+
+#ifdef __ASSEMBLY__
+
+/*
+ * In asm, there are two kinds of code: normal C-type callable functions and
+ * the rest.  The normal callable functions can be called by other code, and
+ * don't do anything unusual with the stack.  Such normal callable functions
+ * are annotated with the ENTRY/ENDPROC macros.  Most asm code falls in this
+ * category.  In this case, no special debugging annotations are needed because
+ * objtool can automatically generate the ORC data for the ORC unwinder to read
+ * at runtime.
+ *
+ * Anything which doesn't fall into the above category, such as syscall and
+ * interrupt handlers, tends to not be called directly by other functions, and
+ * often does unusual non-C-function-type things with the stack pointer.  Such
+ * code needs to be annotated such that objtool can understand it.  The
+ * following CFI hint macros are for this type of code.
+ *
+ * These macros provide hints to objtool about the state of the stack at each
+ * instruction.  Objtool starts from the hints and follows the code flow,
+ * making automatic CFI adjustments when it sees pushes and pops, filling out
+ * the debuginfo as necessary.  It will also warn if it sees any
+ * inconsistencies.
+ */
+.macro UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=0 type=ORC_TYPE_CALL
+#ifdef CONFIG_STACK_VALIDATION
+.Lunwind_hint_ip_\@:
+       .pushsection .discard.unwind_hints
+               /* struct unwind_hint */
+               .long .Lunwind_hint_ip_\@ - .
+               .short \sp_offset
+               .byte \sp_reg
+               .byte \type
+       .popsection
+#endif
+.endm
+
+.macro UNWIND_HINT_EMPTY
+       UNWIND_HINT sp_reg=ORC_REG_UNDEFINED
+.endm
+
+.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 iret=0
+       .if \base == %rsp && \indirect
+               .set sp_reg, ORC_REG_SP_INDIRECT
+       .elseif \base == %rsp
+               .set sp_reg, ORC_REG_SP
+       .elseif \base == %rbp
+               .set sp_reg, ORC_REG_BP
+       .elseif \base == %rdi
+               .set sp_reg, ORC_REG_DI
+       .elseif \base == %rdx
+               .set sp_reg, ORC_REG_DX
+       .elseif \base == %r10
+               .set sp_reg, ORC_REG_R10
+       .else
+               .error "UNWIND_HINT_REGS: bad base register"
+       .endif
+
+       .set sp_offset, \offset
+
+       .if \iret
+               .set type, ORC_TYPE_REGS_IRET
+       .elseif \extra == 0
+               .set type, ORC_TYPE_REGS_IRET
+               .set sp_offset, \offset + (16*8)
+       .else
+               .set type, ORC_TYPE_REGS
+       .endif
+
+       UNWIND_HINT sp_reg=sp_reg sp_offset=sp_offset type=type
+.endm
+
+.macro UNWIND_HINT_IRET_REGS base=%rsp offset=0
+       UNWIND_HINT_REGS base=\base offset=\offset iret=1
+.endm
+
+.macro UNWIND_HINT_FUNC sp_offset=8
+       UNWIND_HINT sp_offset=\sp_offset
+.endm
+
+#else /* !__ASSEMBLY__ */
+
+#define UNWIND_HINT(sp_reg, sp_offset, type)                   \
+       "987: \n\t"                                             \
+       ".pushsection .discard.unwind_hints\n\t"                \
+       /* struct unwind_hint */                                \
+       ".long 987b - .\n\t"                                    \
+       ".short " __stringify(sp_offset) "\n\t"         \
+       ".byte " __stringify(sp_reg) "\n\t"                     \
+       ".byte " __stringify(type) "\n\t"                       \
+       ".popsection\n\t"
+
+#define UNWIND_HINT_SAVE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_SAVE)
+
+#define UNWIND_HINT_RESTORE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_RESTORE)
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_X86_UNWIND_HINTS_H */
 
        diff -I'^#include' arch/x86/insn/inat.h ../../arch/x86/include/asm/inat.h >/dev/null && \
        diff -I'^#include' arch/x86/insn/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) \
        || echo "warning: objtool: x86 instruction decoder differs from kernel" >&2 )) || true
+       @(test -d ../../kernel -a -d ../../tools -a -d ../objtool && (( \
+       diff ../../arch/x86/include/asm/orc_types.h orc_types.h >/dev/null) \
+       || echo "warning: objtool: orc_types.h differs from kernel" >&2 )) || true
        $(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
 
 
 
 static bool ignore_func(struct objtool_file *file, struct symbol *func)
 {
        struct rela *rela;
-       struct instruction *insn;
 
        /* check for STACK_FRAME_NON_STANDARD */
        if (file->whitelist && file->whitelist->rela)
                                return true;
                }
 
-       /* check if it has a context switching instruction */
-       func_for_each_insn(file, func, insn)
-               if (insn->type == INSN_CONTEXT_SWITCH)
-                       return true;
-
        return false;
 }
 
        return 0;
 }
 
+static int read_unwind_hints(struct objtool_file *file)
+{
+       struct section *sec, *relasec;
+       struct rela *rela;
+       struct unwind_hint *hint;
+       struct instruction *insn;
+       struct cfi_reg *cfa;
+       int i;
+
+       sec = find_section_by_name(file->elf, ".discard.unwind_hints");
+       if (!sec)
+               return 0;
+
+       relasec = sec->rela;
+       if (!relasec) {
+               WARN("missing .rela.discard.unwind_hints section");
+               return -1;
+       }
+
+       if (sec->len % sizeof(struct unwind_hint)) {
+               WARN("struct unwind_hint size mismatch");
+               return -1;
+       }
+
+       file->hints = true;
+
+       for (i = 0; i < sec->len / sizeof(struct unwind_hint); i++) {
+               hint = (struct unwind_hint *)sec->data->d_buf + i;
+
+               rela = find_rela_by_dest(sec, i * sizeof(*hint));
+               if (!rela) {
+                       WARN("can't find rela for unwind_hints[%d]", i);
+                       return -1;
+               }
+
+               insn = find_insn(file, rela->sym->sec, rela->addend);
+               if (!insn) {
+                       WARN("can't find insn for unwind_hints[%d]", i);
+                       return -1;
+               }
+
+               cfa = &insn->state.cfa;
+
+               if (hint->type == UNWIND_HINT_TYPE_SAVE) {
+                       insn->save = true;
+                       continue;
+
+               } else if (hint->type == UNWIND_HINT_TYPE_RESTORE) {
+                       insn->restore = true;
+                       insn->hint = true;
+                       continue;
+               }
+
+               insn->hint = true;
+
+               switch (hint->sp_reg) {
+               case ORC_REG_UNDEFINED:
+                       cfa->base = CFI_UNDEFINED;
+                       break;
+               case ORC_REG_SP:
+                       cfa->base = CFI_SP;
+                       break;
+               case ORC_REG_BP:
+                       cfa->base = CFI_BP;
+                       break;
+               case ORC_REG_SP_INDIRECT:
+                       cfa->base = CFI_SP_INDIRECT;
+                       break;
+               case ORC_REG_R10:
+                       cfa->base = CFI_R10;
+                       break;
+               case ORC_REG_R13:
+                       cfa->base = CFI_R13;
+                       break;
+               case ORC_REG_DI:
+                       cfa->base = CFI_DI;
+                       break;
+               case ORC_REG_DX:
+                       cfa->base = CFI_DX;
+                       break;
+               default:
+                       WARN_FUNC("unsupported unwind_hint sp base reg %d",
+                                 insn->sec, insn->offset, hint->sp_reg);
+                       return -1;
+               }
+
+               cfa->offset = hint->sp_offset;
+               insn->state.type = hint->type;
+       }
+
+       return 0;
+}
+
 static int decode_sections(struct objtool_file *file)
 {
        int ret;
        if (ret)
                return ret;
 
+       ret = read_unwind_hints(file);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
                           struct insn_state state)
 {
        struct alternative *alt;
-       struct instruction *insn;
+       struct instruction *insn, *next_insn;
        struct section *sec;
        struct symbol *func = NULL;
        int ret;
        }
 
        while (1) {
+               next_insn = next_insn_same_sec(file, insn);
+
                if (file->c_file && insn->func) {
                        if (func && func != insn->func) {
                                WARN("%s() falls through to next function %s()",
                }
 
                if (insn->visited) {
-                       if (!!insn_state_match(insn, &state))
+                       if (!insn->hint && !insn_state_match(insn, &state))
                                return 1;
 
                        return 0;
                }
 
-               insn->state = state;
+               if (insn->hint) {
+                       if (insn->restore) {
+                               struct instruction *save_insn, *i;
+
+                               i = insn;
+                               save_insn = NULL;
+                               func_for_each_insn_continue_reverse(file, func, i) {
+                                       if (i->save) {
+                                               save_insn = i;
+                                               break;
+                                       }
+                               }
+
+                               if (!save_insn) {
+                                       WARN_FUNC("no corresponding CFI save for CFI restore",
+                                                 sec, insn->offset);
+                                       return 1;
+                               }
+
+                               if (!save_insn->visited) {
+                                       /*
+                                        * Oops, no state to copy yet.
+                                        * Hopefully we can reach this
+                                        * instruction from another branch
+                                        * after the save insn has been
+                                        * visited.
+                                        */
+                                       if (insn == first)
+                                               return 0;
+
+                                       WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
+                                                 sec, insn->offset);
+                                       return 1;
+                               }
+
+                               insn->state = save_insn->state;
+                       }
+
+                       state = insn->state;
+
+               } else
+                       insn->state = state;
 
                insn->visited = true;
 
 
                        return 0;
 
+               case INSN_CONTEXT_SWITCH:
+                       if (func && (!next_insn || !next_insn->hint)) {
+                               WARN_FUNC("unsupported instruction in callable function",
+                                         sec, insn->offset);
+                               return 1;
+                       }
+                       return 0;
+
                case INSN_STACK:
                        if (update_insn_state(insn, &state))
                                return -1;
                if (insn->dead_end)
                        return 0;
 
-               insn = next_insn_same_sec(file, insn);
+               insn = next_insn;
                if (!insn) {
                        WARN("%s: unexpected end of section", sec->name);
                        return 1;
        return 0;
 }
 
+static int validate_unwind_hints(struct objtool_file *file)
+{
+       struct instruction *insn;
+       int ret, warnings = 0;
+       struct insn_state state;
+
+       if (!file->hints)
+               return 0;
+
+       clear_insn_state(&state);
+
+       for_each_insn(file, insn) {
+               if (insn->hint && !insn->visited) {
+                       ret = validate_branch(file, insn, state);
+                       warnings += ret;
+               }
+       }
+
+       return warnings;
+}
+
 static bool is_kasan_insn(struct instruction *insn)
 {
        return (insn->type == INSN_CALL &&
        hash_init(file.insn_hash);
        file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard");
        file.rodata = find_section_by_name(file.elf, ".rodata");
-       file.ignore_unreachables = false;
        file.c_file = find_section_by_name(file.elf, ".comment");
+       file.ignore_unreachables = false;
+       file.hints = false;
 
        arch_initial_func_cfi_state(&initial_func_cfi);
 
                goto out;
        warnings += ret;
 
+       ret = validate_unwind_hints(&file);
+       if (ret < 0)
+               goto out;
+       warnings += ret;
+
        if (!warnings) {
                ret = validate_reachable_instructions(&file);
                if (ret < 0)
 
        unsigned int len;
        unsigned char type;
        unsigned long immediate;
-       bool alt_group, visited, dead_end, ignore;
+       bool alt_group, visited, dead_end, ignore, hint, save, restore;
        struct symbol *call_dest;
        struct instruction *jump_dest;
        struct list_head alts;
        struct list_head insn_list;
        DECLARE_HASHTABLE(insn_hash, 16);
        struct section *rodata, *whitelist;
-       bool ignore_unreachables, c_file;
+       bool ignore_unreachables, c_file, hints;
 };
 
 int check(const char *objname, bool nofp, bool orc);
 
  *
  * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
  * points to the iret return frame.
+ *
+ * The UNWIND_HINT macros are used only for the unwind_hint struct.  They
+ * aren't used in struct orc_entry due to size and complexity constraints.
+ * Objtool converts them to real types when it converts the hints to orc
+ * entries.
  */
 #define ORC_TYPE_CALL                  0
 #define ORC_TYPE_REGS                  1
 #define ORC_TYPE_REGS_IRET             2
+#define UNWIND_HINT_TYPE_SAVE          3
+#define UNWIND_HINT_TYPE_RESTORE       4
 
+#ifndef __ASSEMBLY__
 /*
  * This struct is more or less a vastly simplified version of the DWARF Call
  * Frame Information standard.  It contains only the necessary parts of DWARF
        unsigned        type:2;
 } __packed;
 
+/*
+ * This struct is used by asm and inline asm code to manually annotate the
+ * location of registers on the stack for the ORC unwinder.
+ *
+ * Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*.
+ */
+struct unwind_hint {
+       u32             ip;
+       s16             sp_offset;
+       u8              sp_reg;
+       u8              type;
+};
+#endif /* __ASSEMBLY__ */
+
 #endif /* _ORC_TYPES_H */