Add support for BTI.

Add support for Branch Target Identification. This also adds an extension to
Bind() to indicate which instruction should be used as the landing pad - one of
the BTIs or a PAuth instruction.

The simulation support is partial; memory may only be guarded globally or not at
all.

Change-Id: Idb54826dcf866f1e677fe1579d05987621eae721
diff --git a/src/aarch64/assembler-aarch64.cc b/src/aarch64/assembler-aarch64.cc
index 53f3c11..4dffb89 100644
--- a/src/aarch64/assembler-aarch64.cc
+++ b/src/aarch64/assembler-aarch64.cc
@@ -2488,6 +2488,12 @@
   Emit(AUTIBSP);
 }
 
+void Assembler::bti(BranchTargetIdentifier id) {
+  VIXL_ASSERT((id != EmitPACIASP) && (id != EmitPACIBSP));  // Not modes of Bti.
+  VIXL_ASSERT(id != EmitBTI_none);  // Always generate an instruction.
+  VIXL_ASSERT(CPUHas(CPUFeatures::kBTI));
+  hint(static_cast<SystemHint>(id));
+}
 
 void Assembler::mvn(const Register& rd, const Operand& operand) {
   orn(rd, AppropriateZeroRegFor(rd), operand);
diff --git a/src/aarch64/assembler-aarch64.h b/src/aarch64/assembler-aarch64.h
index b583089..ff00d1c 100644
--- a/src/aarch64/assembler-aarch64.h
+++ b/src/aarch64/assembler-aarch64.h
@@ -2141,10 +2141,12 @@
   // Conditional speculation dependency barrier.
   void csdb();
 
-  // Alias for system instructions.
   // No-op.
   void nop() { hint(NOP); }
 
+  // Branch target identification.
+  void bti(BranchTargetIdentifier id);
+
   // FP and NEON instructions.
   // Move double precision immediate to FP register.
   void fmov(const VRegister& vd, double imm);
diff --git a/src/aarch64/constants-aarch64.h b/src/aarch64/constants-aarch64.h
index 63c06bb..31a26e0 100644
--- a/src/aarch64/constants-aarch64.h
+++ b/src/aarch64/constants-aarch64.h
@@ -300,14 +300,31 @@
 };
 
 enum SystemHint {
-  NOP   = 0,
-  YIELD = 1,
-  WFE   = 2,
-  WFI   = 3,
-  SEV   = 4,
-  SEVL  = 5,
-  ESB   = 16,
-  CSDB  = 20
+  NOP    = 0,
+  YIELD  = 1,
+  WFE    = 2,
+  WFI    = 3,
+  SEV    = 4,
+  SEVL   = 5,
+  ESB    = 16,
+  CSDB   = 20,
+  BTI    = 32,
+  BTI_c  = 34,
+  BTI_j  = 36,
+  BTI_jc = 38
+};
+
+enum BranchTargetIdentifier {
+  EmitBTI_none = NOP,
+  EmitBTI = BTI,
+  EmitBTI_c = BTI_c,
+  EmitBTI_j = BTI_j,
+  EmitBTI_jc = BTI_jc,
+
+  // These correspond to the values of the CRm:op2 fields in the equivalent HINT
+  // instruction.
+  EmitPACIASP = 25,
+  EmitPACIBSP = 27
 };
 
 enum BarrierDomain {
@@ -347,6 +364,23 @@
   PSTL3STRM = 0x15
 };
 
+enum BType {
+  // Set when executing any instruction on a guarded page, except those cases
+  // listed below.
+  DefaultBType = 0,
+
+  // Set when an indirect branch is taken from an unguarded page to a guarded
+  // page, or from a guarded page to ip0 or ip1 (x16 or x17), eg "br ip0".
+  BranchFromUnguardedOrToIP = 1,
+
+  // Set when an indirect branch and link (call) is taken, eg. "blr x0".
+  BranchAndLink = 2,
+
+  // Set when an indirect branch is taken from a guarded page to a register
+  // that is not ip0 or ip1 (x16 or x17), eg, "br x0".
+  BranchFromGuardedNotToIP = 3
+};
+
 template<int op0, int op1, int crn, int crm, int op2>
 class SystemRegisterEncoder {
  public:
diff --git a/src/aarch64/cpu-features-auditor-aarch64.cc b/src/aarch64/cpu-features-auditor-aarch64.cc
index 726676f..314237b 100644
--- a/src/aarch64/cpu-features-auditor-aarch64.cc
+++ b/src/aarch64/cpu-features-auditor-aarch64.cc
@@ -1023,7 +1023,19 @@
         required.Combine(CPUFeatures::kPAuth);
         break;
       default:
-        if (instr->GetImmHint() == ESB) required.Combine(CPUFeatures::kRAS);
+        switch (instr->GetImmHint()) {
+          case ESB:
+            required.Combine(CPUFeatures::kRAS);
+            break;
+          case BTI:
+          case BTI_j:
+          case BTI_c:
+          case BTI_jc:
+            required.Combine(CPUFeatures::kBTI);
+            break;
+          default:
+            break;
+        }
         break;
     }
 
diff --git a/src/aarch64/disasm-aarch64.cc b/src/aarch64/disasm-aarch64.cc
index 209f668..b0444cd 100644
--- a/src/aarch64/disasm-aarch64.cc
+++ b/src/aarch64/disasm-aarch64.cc
@@ -2033,53 +2033,49 @@
       }
     }
   } else if (instr->Mask(SystemHintFMask) == SystemHintFixed) {
+    form = NULL;
     switch (instr->GetImmHint()) {
-      case NOP: {
-        form = NULL;
+      case NOP:
         mnemonic = "nop";
         break;
-      }
-      case YIELD: {
-        form = NULL;
+      case YIELD:
         mnemonic = "yield";
         break;
-      }
-      case WFE: {
-        form = NULL;
+      case WFE:
         mnemonic = "wfe";
         break;
-      }
-      case WFI: {
-        form = NULL;
+      case WFI:
         mnemonic = "wfi";
         break;
-      }
-      case SEV: {
-        form = NULL;
+      case SEV:
         mnemonic = "sev";
         break;
-      }
-      case SEVL: {
-        form = NULL;
+      case SEVL:
         mnemonic = "sevl";
         break;
-      }
-      case ESB: {
-        form = NULL;
+      case ESB:
         mnemonic = "esb";
         break;
-      }
-      case CSDB: {
-        form = NULL;
+      case CSDB:
         mnemonic = "csdb";
         break;
-      }
-      default: {
+      case BTI:
+        mnemonic = "bti";
+        break;
+      case BTI_c:
+        mnemonic = "bti c";
+        break;
+      case BTI_j:
+        mnemonic = "bti j";
+        break;
+      case BTI_jc:
+        mnemonic = "bti jc";
+        break;
+      default:
         // Fall back to 'hint #<imm7>'.
         form = "'IH";
         mnemonic = "hint";
         break;
-      }
     }
   } else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) {
     switch (instr->Mask(MemBarrierMask)) {
diff --git a/src/aarch64/instructions-aarch64.h b/src/aarch64/instructions-aarch64.h
index c696403..7c969f1 100644
--- a/src/aarch64/instructions-aarch64.h
+++ b/src/aarch64/instructions-aarch64.h
@@ -312,6 +312,24 @@
            (Mask(MoveWideImmediateMask) == MOVN_w);
   }
 
+  bool IsException() const { return Mask(ExceptionFMask) == ExceptionFixed; }
+
+  bool IsPAuth() const { return Mask(SystemPAuthFMask) == SystemPAuthFixed; }
+
+  bool IsBti() const {
+    if (Mask(SystemHintFMask) == SystemHintFixed) {
+      int imm_hint = GetImmHint();
+      switch (imm_hint) {
+        case BTI:
+        case BTI_c:
+        case BTI_j:
+        case BTI_jc:
+          return true;
+      }
+    }
+    return false;
+  }
+
   static int GetImmBranchRangeBitwidth(ImmBranchType branch_type);
   VIXL_DEPRECATED(
       "GetImmBranchRangeBitwidth",
diff --git a/src/aarch64/macro-assembler-aarch64.cc b/src/aarch64/macro-assembler-aarch64.cc
index e881a81..2b5e665 100644
--- a/src/aarch64/macro-assembler-aarch64.cc
+++ b/src/aarch64/macro-assembler-aarch64.cc
@@ -711,14 +711,26 @@
   }
 }
 
-
-void MacroAssembler::Bind(Label* label) {
+void MacroAssembler::Bind(Label* label, BranchTargetIdentifier id) {
   VIXL_ASSERT(allow_macro_instructions_);
   veneer_pool_.DeleteUnresolvedBranchInfoForLabel(label);
-  bind(label);
+  if (id == EmitBTI_none) {
+    bind(label);
+  } else {
+    // Emit this inside an ExactAssemblyScope to ensure there are no extra
+    // instructions between the bind and the target identifier instruction.
+    ExactAssemblyScope scope(this, kInstructionSize);
+    bind(label);
+    if (id == EmitPACIASP) {
+      paciasp();
+    } else if (id == EmitPACIBSP) {
+      pacibsp();
+    } else {
+      bti(id);
+    }
+  }
 }
 
-
 // Bind a label to a specified offset from the start of the buffer.
 void MacroAssembler::BindToOffset(Label* label, ptrdiff_t offset) {
   VIXL_ASSERT(allow_macro_instructions_);
diff --git a/src/aarch64/macro-assembler-aarch64.h b/src/aarch64/macro-assembler-aarch64.h
index 8c0be9f..250359d 100644
--- a/src/aarch64/macro-assembler-aarch64.h
+++ b/src/aarch64/macro-assembler-aarch64.h
@@ -1051,7 +1051,7 @@
     SingleEmissionCheckScope guard(this);
     bfxil(rd, rn, lsb, width);
   }
-  void Bind(Label* label);
+  void Bind(Label* label, BranchTargetIdentifier id = EmitBTI_none);
   // Bind a label to a specified offset from the start of the buffer.
   void BindToOffset(Label* label, ptrdiff_t offset);
   void Bl(Label* label) {
diff --git a/src/aarch64/simulator-aarch64.cc b/src/aarch64/simulator-aarch64.cc
index 01130bc..1352585 100644
--- a/src/aarch64/simulator-aarch64.cc
+++ b/src/aarch64/simulator-aarch64.cc
@@ -111,6 +111,8 @@
   // time they are encountered. This warning can be silenced using
   // SilenceExclusiveAccessWarning().
   print_exclusive_access_warning_ = true;
+
+  guard_pages_ = false;
 }
 
 
@@ -141,6 +143,9 @@
   }
   // Returning to address 0 exits the Simulator.
   WriteLr(kEndOfSimAddress);
+
+  btype_ = DefaultBType;
+  next_btype_ = DefaultBType;
 }
 
 
@@ -1085,13 +1090,33 @@
   }
 }
 
+BType Simulator::GetBTypeFromInstruction(const Instruction* instr) const {
+  switch (instr->Mask(UnconditionalBranchToRegisterMask)) {
+    case BLR:
+    case BLRAA:
+    case BLRAB:
+    case BLRAAZ:
+    case BLRABZ:
+      return BranchAndLink;
+    case BR:
+    case BRAA:
+    case BRAB:
+    case BRAAZ:
+    case BRABZ:
+      if ((instr->GetRn() == 16) || (instr->GetRn() == 17) ||
+          !PcIsInGuardedPage()) {
+        return BranchFromUnguardedOrToIP;
+      }
+      return BranchFromGuardedNotToIP;
+  }
+  return DefaultBType;
+}
 
 void Simulator::VisitUnconditionalBranchToRegister(const Instruction* instr) {
   bool authenticate = false;
   bool link = false;
-  uint64_t addr = 0;
+  uint64_t addr = ReadXRegister(instr->GetRn());
   uint64_t context = 0;
-  Instruction* target;
 
   switch (instr->Mask(UnconditionalBranchToRegisterMask)) {
     case BLR:
@@ -1099,7 +1124,6 @@
       VIXL_FALLTHROUGH();
     case BR:
     case RET:
-      addr = ReadXRegister(instr->GetRn());
       break;
 
     case BLRAAZ:
@@ -1109,7 +1133,6 @@
     case BRAAZ:
     case BRABZ:
       authenticate = true;
-      addr = ReadXRegister(instr->GetRn());
       break;
 
     case BLRAA:
@@ -1119,7 +1142,6 @@
     case BRAA:
     case BRAB:
       authenticate = true;
-      addr = ReadXRegister(instr->GetRn());
       context = ReadXRegister(instr->GetRd());
       break;
 
@@ -1147,8 +1169,8 @@
     }
   }
 
-  target = Instruction::Cast(addr);
-  WritePc(target);
+  WritePc(Instruction::Cast(addr));
+  WriteNextBType(GetBTypeFromInstruction(instr));
 }
 
 
@@ -3643,6 +3665,25 @@
   if (instr->GetInstructionBits() == XPACLRI) {
     WriteXRegister(30, StripPAC(ReadXRegister(30), kInstructionPointer));
   } else if (instr->Mask(SystemPAuthFMask) == SystemPAuthFixed) {
+    // Check BType allows PACI[AB]SP instructions.
+    if (PcIsInGuardedPage()) {
+      Instr i = instr->Mask(SystemPAuthMask);
+      if ((i == PACIASP) || (i == PACIBSP)) {
+        switch (ReadBType()) {
+          case DefaultBType:
+            VIXL_ABORT_WITH_MSG("Executing PACIXSP with wrong BType.");
+            break;
+          case BranchFromGuardedNotToIP:
+          // TODO: This case depends on the value of SCTLR_EL1.BT0, which we
+          // assume here to be zero. This allows execution of PACI[AB]SP when
+          // BTYPE is BranchFromGuardedNotToIP (0b11).
+          case BranchFromUnguardedOrToIP:
+          case BranchAndLink:
+            break;
+        }
+      }
+    }
+
     switch (instr->Mask(SystemPAuthMask)) {
 #define DEFINE_PAUTH_FUNCS(SUFFIX, DST, MOD, KEY)                              \
   case PACI##SUFFIX:                                                           \
@@ -3707,6 +3748,22 @@
       case NOP:
       case ESB:
       case CSDB:
+      case BTI_jc:
+        break;
+      case BTI:
+        if (PcIsInGuardedPage() && (ReadBType() != DefaultBType)) {
+          VIXL_ABORT_WITH_MSG("Executing BTI with wrong BType.");
+        }
+        break;
+      case BTI_c:
+        if (PcIsInGuardedPage() && (ReadBType() == BranchFromGuardedNotToIP)) {
+          VIXL_ABORT_WITH_MSG("Executing BTI c with wrong BType.");
+        }
+        break;
+      case BTI_j:
+        if (PcIsInGuardedPage() && (ReadBType() == BranchAndLink)) {
+          VIXL_ABORT_WITH_MSG("Executing BTI j with wrong BType.");
+        }
         break;
       default:
         VIXL_UNIMPLEMENTED();
diff --git a/src/aarch64/simulator-aarch64.h b/src/aarch64/simulator-aarch64.h
index 5f1f632..11ee07c 100644
--- a/src/aarch64/simulator-aarch64.h
+++ b/src/aarch64/simulator-aarch64.h
@@ -715,11 +715,38 @@
   }
   VIXL_DEPRECATED("IncrementPc", void increment_pc()) { IncrementPc(); }
 
+  BType ReadBType() const { return btype_; }
+  void WriteNextBType(BType btype) { next_btype_ = btype; }
+  void UpdateBType() {
+    btype_ = next_btype_;
+    next_btype_ = DefaultBType;
+  }
+
+  // Helper function to determine BType for branches.
+  BType GetBTypeFromInstruction(const Instruction* instr) const;
+
+  bool PcIsInGuardedPage() const { return guard_pages_; }
+  void SetGuardedPages(bool guard_pages) { guard_pages_ = guard_pages; }
+
   void ExecuteInstruction() {
     // The program counter should always be aligned.
     VIXL_ASSERT(IsWordAligned(pc_));
     pc_modified_ = false;
 
+    // On guarded pages, if BType is not zero, take an exception on any
+    // instruction other than BTI, PACI[AB]SP, HLT or BRK.
+    if (PcIsInGuardedPage() && (ReadBType() != DefaultBType)) {
+      if (pc_->IsPAuth()) {
+        Instr i = pc_->Mask(SystemPAuthMask);
+        if ((i != PACIASP) && (i != PACIBSP)) {
+          VIXL_ABORT_WITH_MSG(
+              "Executing non-BTI instruction with wrong BType.");
+        }
+      } else if (!pc_->IsBti() && !pc_->IsException()) {
+        VIXL_ABORT_WITH_MSG("Executing non-BTI instruction with wrong BType.");
+      }
+    }
+
     // decoder_->Decode(...) triggers at least the following visitors:
     //  1. The CPUFeaturesAuditor (`cpu_features_auditor_`).
     //  2. The PrintDisassembler (`print_disasm_`), if enabled.
@@ -729,6 +756,7 @@
     decoder_->Decode(pc_);
     IncrementPc();
     LogAllWrittenRegisters();
+    UpdateBType();
 
     VIXL_CHECK(cpu_features_auditor_.InstructionIsAvailable());
   }
@@ -3197,6 +3225,17 @@
   bool pc_modified_;
   const Instruction* pc_;
 
+  // Branch type register, used for branch target identification.
+  BType btype_;
+
+  // Next value of branch type register after the current instruction has been
+  // decoded.
+  BType next_btype_;
+
+  // Global flag for enabling guarded pages.
+  // TODO: implement guarding at page granularity, rather than globally.
+  bool guard_pages_;
+
   static const char* xreg_names[];
   static const char* wreg_names[];
   static const char* hreg_names[];
diff --git a/src/cpu-features.h b/src/cpu-features.h
index 9dac583..88b6914 100644
--- a/src/cpu-features.h
+++ b/src/cpu-features.h
@@ -94,7 +94,9 @@
   /* FP16 fused multiply-add or -subtract long: FMLAL{2}, FMLSL{2}.         */ \
   V(kFHM,                 "FHM",                    "asimdfhm")                \
   /* Data-independent timing (for selected instructions).                   */ \
-  V(kDIT,                 "DIT",                    "dit")
+  V(kDIT,                 "DIT",                    "dit")                     \
+  /* Branch target identification.                                          */ \
+  V(kBTI,                 "BTI",                    NULL)
 // clang-format on