[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: Ross McIlroy <rmcilroy@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#66082}
This commit is contained in:
parent
ab6c4669ba
commit
4eac274d32
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user