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