Commit 4eac274d authored by Georgia Kouveli's avatar Georgia Kouveli Committed by Commit Bot

[arm64] Add support for BTI instruction

Bug: v8:10026
Change-Id: I8ee836ee6298415a21cf487bc3d0e5f803fc6186
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1965590
Commit-Queue: Georgia Kouveli <georgia.kouveli@arm.com>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66082}
parent ab6c4669
......@@ -1184,6 +1184,30 @@ void Assembler::autia1716() { Emit(AUTIA1716); }
void Assembler::paciasp() { Emit(PACIASP); }
void Assembler::autiasp() { Emit(AUTIASP); }
void Assembler::bti(BranchTargetIdentifier id) {
SystemHint op;
switch (id) {
case BranchTargetIdentifier::kBti:
op = BTI;
break;
case BranchTargetIdentifier::kBtiCall:
op = BTI_c;
break;
case BranchTargetIdentifier::kBtiJump:
op = BTI_j;
break;
case BranchTargetIdentifier::kBtiJumpCall:
op = BTI_jc;
break;
case BranchTargetIdentifier::kNone:
case BranchTargetIdentifier::kPaciasp:
// We always want to generate a BTI instruction here, so disallow
// skipping its generation or generating a PACIASP instead.
UNREACHABLE();
}
hint(op);
}
void Assembler::ldp(const CPURegister& rt, const CPURegister& rt2,
const MemOperand& src) {
LoadStorePair(rt, rt2, src, LoadPairOpFor(rt, rt2));
......
......@@ -953,7 +953,10 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
// Conditional speculation barrier.
void csdb();
// Alias for system instructions.
// Branch target identification.
void bti(BranchTargetIdentifier id);
// No-op.
void nop() { hint(NOP); }
// Different nop operations are used by the code generator to detect certain
......
......@@ -389,7 +389,36 @@ enum SystemHint {
WFI = 3,
SEV = 4,
SEVL = 5,
CSDB = 20
CSDB = 20,
BTI = 32,
BTI_c = 34,
BTI_j = 36,
BTI_jc = 38
};
// In a guarded page, only BTI and PACI[AB]SP instructions are allowed to be
// the target of indirect branches. Details on which kinds of branches each
// instruction allows follow in the comments below:
enum class BranchTargetIdentifier {
// Do not emit a BTI instruction.
kNone,
// Emit a BTI instruction. Cannot be the target of indirect jumps/calls.
kBti,
// Emit a "BTI c" instruction. Can be the target of indirect jumps (BR) with
// x16/x17 as the target register, or indirect calls (BLR).
kBtiCall,
// Emit a "BTI j" instruction. Can be the target of indirect jumps (BR).
kBtiJump,
// Emit a "BTI jc" instruction, which is a combination of "BTI j" and "BTI c".
kBtiJumpCall,
// Emit a PACIASP instruction, which acts like a "BTI c" or a "BTI jc", based
// on the value of SCTLR_EL1.BT0.
kPaciasp
};
enum BarrierDomain {
......
......@@ -379,6 +379,24 @@ class Instruction {
(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 = ImmHint();
switch (imm_hint) {
case BTI:
case BTI_c:
case BTI_j:
case BTI_jc:
return true;
}
}
return false;
}
bool IsNop(int n) {
// A marking nop is an instruction
// mov r<n>, r<n>
......
......@@ -309,9 +309,21 @@ void MacroAssembler::Bfxil(const Register& rd, const Register& rn, unsigned lsb,
bfxil(rd, rn, lsb, width);
}
void TurboAssembler::Bind(Label* label) {
void TurboAssembler::Bind(Label* label, BranchTargetIdentifier id) {
DCHECK(allow_macro_instructions());
bind(label);
if (id == BranchTargetIdentifier::kNone) {
bind(label);
} else {
// Emit this inside an InstructionAccurateScope to ensure there are no extra
// instructions between the bind and the target identifier instruction.
InstructionAccurateScope scope(this, 1);
bind(label);
if (id == BranchTargetIdentifier::kPaciasp) {
paciasp();
} else {
bti(id);
}
}
}
void TurboAssembler::Bl(Label* label) {
......
......@@ -630,7 +630,8 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
// Returns false, otherwise.
bool TryOneInstrMoveImmediate(const Register& dst, int64_t imm);
inline void Bind(Label* label);
inline void Bind(Label* label,
BranchTargetIdentifier id = BranchTargetIdentifier::kNone);
static unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size);
......@@ -2043,7 +2044,7 @@ class UseScratchRegisterScope {
CPURegList list(reg1, reg2);
Include(list);
}
void Exclude(const Register& reg1, const Register& reg2) {
void Exclude(const Register& reg1, const Register& reg2 = NoReg) {
CPURegList list(reg1, reg2);
Exclude(list);
}
......
......@@ -1436,7 +1436,7 @@ void DisassemblingDecoder::VisitSystem(Instruction* instr) {
#define PAUTH_CASE(NAME, MN) \
case NAME: \
mnemonic = MN; \
form = NULL; \
form = nullptr; \
break;
PAUTH_SYSTEM_MNEMONICS(PAUTH_CASE)
......@@ -1478,17 +1478,30 @@ void DisassemblingDecoder::VisitSystem(Instruction* instr) {
}
} else if (instr->Mask(SystemHintFMask) == SystemHintFixed) {
DCHECK(instr->Mask(SystemHintMask) == HINT);
form = nullptr;
switch (instr->ImmHint()) {
case NOP: {
case NOP:
mnemonic = "nop";
form = nullptr;
break;
}
case CSDB: {
case CSDB:
mnemonic = "csdb";
form = nullptr;
break;
}
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";
}
} else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) {
switch (instr->Mask(MemBarrierMask)) {
......
......@@ -298,6 +298,7 @@ void Simulator::SetRedirectInstruction(Instruction* instruction) {
Simulator::Simulator(Decoder<DispatchingDecoderVisitor>* decoder,
Isolate* isolate, FILE* stream)
: decoder_(decoder),
guard_pages_(false),
last_debugger_input_(nullptr),
log_parameters_(NO_PARAM),
isolate_(isolate) {
......@@ -314,6 +315,7 @@ Simulator::Simulator(Decoder<DispatchingDecoderVisitor>* decoder,
Simulator::Simulator()
: decoder_(nullptr),
guard_pages_(false),
last_debugger_input_(nullptr),
log_parameters_(NO_PARAM),
isolate_(nullptr) {
......@@ -361,6 +363,8 @@ void Simulator::ResetState() {
// Reset debug helpers.
breakpoints_.clear();
break_on_next_ = false;
btype_ = DefaultBType;
}
Simulator::~Simulator() {
......@@ -1494,6 +1498,20 @@ void Simulator::VisitConditionalBranch(Instruction* instr) {
}
}
Simulator::BType Simulator::GetBTypeFromInstruction(
const Instruction* instr) const {
switch (instr->Mask(UnconditionalBranchToRegisterMask)) {
case BLR:
return BranchAndLink;
case BR:
if (!PcIsInGuardedPage() || (instr->Rn() == 16) || (instr->Rn() == 17)) {
return BranchFromUnguardedOrToIP;
}
return BranchFromGuardedNotToIP;
}
return DefaultBType;
}
void Simulator::VisitUnconditionalBranchToRegister(Instruction* instr) {
Instruction* target = reg<Instruction*>(instr->Rn());
switch (instr->Mask(UnconditionalBranchToRegisterMask)) {
......@@ -1513,6 +1531,7 @@ void Simulator::VisitUnconditionalBranchToRegister(Instruction* instr) {
default:
UNIMPLEMENTED();
}
set_btype(GetBTypeFromInstruction(instr));
}
void Simulator::VisitTestBranch(Instruction* instr) {
......@@ -3096,6 +3115,7 @@ void Simulator::VisitSystem(Instruction* instr) {
// range of immediates instead of indicating a different instruction. This
// makes the decoding tricky.
if (instr->Mask(SystemPAuthFMask) == SystemPAuthFixed) {
// The BType check for PACIASP happens in CheckBType().
switch (instr->Mask(SystemPAuthMask)) {
#define DEFINE_PAUTH_FUNCS(SUFFIX, DST, MOD, KEY) \
case PACI##SUFFIX: \
......@@ -3145,6 +3165,11 @@ void Simulator::VisitSystem(Instruction* instr) {
switch (instr->ImmHint()) {
case NOP:
case CSDB:
case BTI_jc:
case BTI:
case BTI_c:
case BTI_j:
// The BType checks happen in CheckBType().
break;
default:
UNIMPLEMENTED();
......
......@@ -770,8 +770,125 @@ class Simulator : public DecoderVisitor, public SimulatorBase {
virtual void Decode(Instruction* instr) { decoder_->Decode(instr); }
// Branch Target Identification (BTI)
//
// Executing an instruction updates PSTATE.BTYPE, as described in the table
// below. Execution of an instruction on a guarded page is allowed if either:
// * PSTATE.BTYPE is 00, or
// * it is a BTI or PACI[AB]SP instruction that accepts the current value of
// PSTATE.BTYPE (as described in the table below), or
// * it is BRK or HLT instruction that causes some higher-priority exception.
//
// --------------------------------------------------------------------------
// | Last-executed instruction | Sets | Accepted by |
// | | BTYPE to | BTI | BTI j | BTI c | BTI jc |
// --------------------------------------------------------------------------
// | - BR from an unguarded page. | | | | | |
// | - BR from guarded page, | | | | | |
// | to x16 or x17. | 01 | | X | X | X |
// --------------------------------------------------------------------------
// | BR from guarded page, | | | | | |
// | not to x16 or x17. | 11 | | X | | X |
// --------------------------------------------------------------------------
// | BLR | 10 | | | X | X |
// --------------------------------------------------------------------------
// | Any other instruction | | | | | |
// |(including RET). | 00 | X | X | X | X |
// --------------------------------------------------------------------------
//
// PACI[AB]SP is treated either like "BTI c" or "BTI jc", according to the
// value of SCTLR_EL1.BT0. Details available in ARM DDI 0487E.a, D5-2580.
enum BType {
// Set when executing any instruction, except those cases listed below.
DefaultBType = 0,
// Set when an indirect branch is taken from an unguarded 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
};
BType btype() const { return btype_; }
void ResetBType() { btype_ = DefaultBType; }
void set_btype(BType btype) { btype_ = btype; }
// 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 CheckBTypeForPAuth() {
DCHECK(pc_->IsPAuth());
Instr instr = pc_->Mask(SystemPAuthMask);
// Only PACI[AB]SP allowed here, but we don't currently support PACIBSP.
CHECK_EQ(instr, PACIASP);
// Check BType allows PACI[AB]SP instructions.
switch (btype()) {
case BranchFromGuardedNotToIP:
// This case depends on the value of SCTLR_EL1.BT0, which we assume
// here to be set. This makes PACI[AB]SP behave like "BTI c",
// disallowing its execution when BTYPE is BranchFromGuardedNotToIP
// (0b11).
FATAL("Executing PACIASP with wrong BType.");
case BranchFromUnguardedOrToIP:
case BranchAndLink:
break;
case DefaultBType:
UNREACHABLE();
}
}
void CheckBTypeForBti() {
DCHECK(pc_->IsBti());
switch (pc_->ImmHint()) {
case BTI_jc:
break;
case BTI: {
DCHECK(btype() != DefaultBType);
FATAL("Executing BTI with wrong BType (expected 0, got %d).", btype());
break;
}
case BTI_c:
if (btype() == BranchFromGuardedNotToIP) {
FATAL("Executing BTI c with wrong BType (3).");
}
break;
case BTI_j:
if (btype() == BranchAndLink) {
FATAL("Executing BTI j with wrong BType (2).");
}
break;
default:
UNIMPLEMENTED();
}
}
void CheckBType() {
// 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() && (btype() != DefaultBType)) {
if (pc_->IsPAuth()) {
CheckBTypeForPAuth();
} else if (pc_->IsBti()) {
CheckBTypeForBti();
} else if (!pc_->IsException()) {
FATAL("Executing non-BTI instruction with wrong BType.");
}
}
}
void ExecuteInstruction() {
DCHECK(IsAligned(reinterpret_cast<uintptr_t>(pc_), kInstrSize));
CheckBType();
ResetBType();
CheckBreakNext();
Decode(pc_);
increment_pc();
......@@ -2192,6 +2309,13 @@ class Simulator : public DecoderVisitor, public SimulatorBase {
bool pc_modified_;
Instruction* pc_;
// Branch type register, used for branch target identification.
BType btype_;
// Global flag for enabling guarded pages.
// TODO(arm64): implement guarding at page granularity, rather than globally.
bool guard_pages_;
static const char* xreg_names[];
static const char* wreg_names[];
static const char* sreg_names[];
......
......@@ -1873,6 +1873,76 @@ TEST(branch_to_reg) {
CHECK_EQUAL_64(84, x2);
}
static void BtiHelper(Register ipreg) {
SETUP();
Label jump_target, jump_call_target, call_target, done;
START();
UseScratchRegisterScope temps(&masm);
temps.Exclude(ipreg);
__ Adr(x0, &jump_target);
__ Br(x0);
__ Nop();
__ Bind(&jump_target, BranchTargetIdentifier::kBtiJump);
__ Adr(x0, &call_target);
__ Blr(x0);
__ Adr(ipreg, &jump_call_target);
__ Blr(ipreg);
__ Adr(lr, &done); // Make Ret return to done label.
__ Br(ipreg);
__ Bind(&call_target, BranchTargetIdentifier::kBtiCall);
__ Ret();
__ Bind(&jump_call_target, BranchTargetIdentifier::kBtiJumpCall);
__ Ret();
__ Bind(&done);
END();
#ifdef USE_SIMULATOR
simulator.SetGuardedPages(true);
RUN();
#endif // USE_SIMULATOR
}
TEST(bti) {
BtiHelper(x16);
BtiHelper(x17);
}
TEST(unguarded_bti_is_nop) {
SETUP();
Label start, none, c, j, jc;
START();
__ B(&start);
__ Bind(&none, BranchTargetIdentifier::kBti);
__ Bind(&c, BranchTargetIdentifier::kBtiCall);
__ Bind(&j, BranchTargetIdentifier::kBtiJump);
__ Bind(&jc, BranchTargetIdentifier::kBtiJumpCall);
CHECK(__ SizeOfCodeGeneratedSince(&none) == 4 * kInstrSize);
__ Ret();
Label jump_to_c, call_to_j;
__ Bind(&start);
__ Adr(x0, &none);
__ Adr(lr, &jump_to_c);
__ Br(x0);
__ Bind(&jump_to_c);
__ Adr(x0, &c);
__ Adr(lr, &call_to_j);
__ Br(x0);
__ Bind(&call_to_j);
__ Adr(x0, &j);
__ Blr(x0);
END();
#ifdef USE_SIMULATOR
simulator.SetGuardedPages(false);
RUN();
#endif // USE_SIMULATOR
}
TEST(compare_branch) {
INIT_V8();
SETUP();
......
......@@ -1874,11 +1874,45 @@ TEST_(system_msr) {
TEST_(system_nop) {
SET_UP_ASM();
{
SET_UP_ASM();
COMPARE(nop(), "nop");
CLEANUP();
}
{
SET_UP_MASM();
COMPARE(Nop(), "nop");
CLEANUP();
}
}
COMPARE(nop(), "nop");
TEST_(bti) {
{
SET_UP_ASM();
CLEANUP();
COMPARE(bti(BranchTargetIdentifier::kBti), "bti");
COMPARE(bti(BranchTargetIdentifier::kBtiCall), "bti c");
COMPARE(bti(BranchTargetIdentifier::kBtiJump), "bti j");
COMPARE(bti(BranchTargetIdentifier::kBtiJumpCall), "bti jc");
COMPARE(hint(BTI), "bti");
COMPARE(hint(BTI_c), "bti c");
COMPARE(hint(BTI_j), "bti j");
COMPARE(hint(BTI_jc), "bti jc");
CLEANUP();
}
{
SET_UP_MASM();
Label dummy1, dummy2, dummy3, dummy4;
COMPARE(Bind(&dummy1, BranchTargetIdentifier::kBti), "bti");
COMPARE(Bind(&dummy2, BranchTargetIdentifier::kBtiCall), "bti c");
COMPARE(Bind(&dummy3, BranchTargetIdentifier::kBtiJump), "bti j");
COMPARE(Bind(&dummy4, BranchTargetIdentifier::kBtiJumpCall), "bti jc");
CLEANUP();
}
}
TEST(system_pauth) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment