blob: 299b6e46a2a19ffdee91a343515168e250b5e47e [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "gtest/gtest.h"
#include <unistd.h>
#include <cstdint>
#include <initializer_list>
#include <tuple>
#include <type_traits>
#include "berberis/base/bit_util.h"
#include "berberis/guest_state/guest_addr.h"
#include "berberis/guest_state/guest_state_riscv64.h"
#include "berberis/interpreter/riscv64/interpreter.h"
namespace berberis {
namespace {
class Riscv64InterpreterTest : public ::testing::Test {
public:
void InterpretCFld(uint16_t insn_bytes, uint64_t offset) {
auto code_start = ToGuestAddr(&insn_bytes);
state_.cpu.insn_addr = code_start;
SetXReg<8>(state_.cpu, ToGuestAddr(bit_cast<uint8_t*>(&kDataToLoad) - offset));
InterpretInsn(&state_);
EXPECT_EQ(GetFReg<8>(state_.cpu), kDataToLoad);
}
void InterpretCAddi4spn(uint16_t insn_bytes, uint64_t expected_offset) {
auto code_start = ToGuestAddr(&insn_bytes);
state_.cpu.insn_addr = code_start;
SetXReg<2>(state_.cpu, 1);
InterpretInsn(&state_);
EXPECT_EQ(GetXReg<9>(state_.cpu), 1 + expected_offset);
}
void InterpretCAddi(uint16_t insn_bytes, uint64_t expected_increment) {
auto code_start = ToGuestAddr(&insn_bytes);
state_.cpu.insn_addr = code_start;
SetXReg<2>(state_.cpu, 1);
InterpretInsn(&state_);
EXPECT_EQ(GetXReg<2>(state_.cpu), 1 + expected_increment);
}
void InterpretCJ(uint16_t insn_bytes, int16_t expected_offset) {
auto code_start = ToGuestAddr(&insn_bytes);
state_.cpu.insn_addr = code_start;
InterpretInsn(&state_);
EXPECT_EQ(state_.cpu.insn_addr, code_start + expected_offset);
}
void InterpretOp(uint32_t insn_bytes,
std::initializer_list<std::tuple<uint64_t, uint64_t, uint64_t>> args) {
for (auto [arg1, arg2, expected_result] : args) {
state_.cpu.insn_addr = ToGuestAddr(&insn_bytes);
SetXReg<2>(state_.cpu, arg1);
SetXReg<3>(state_.cpu, arg2);
InterpretInsn(&state_);
EXPECT_EQ(GetXReg<1>(state_.cpu), expected_result);
}
}
void InterpretFence(uint32_t insn_bytes) {
state_.cpu.insn_addr = ToGuestAddr(&insn_bytes);
InterpretInsn(&state_);
}
void InterpretAmo(uint32_t insn_bytes, uint64_t arg1, uint64_t arg2, uint64_t expected_result,
uint64_t expected_memory) {
state_.cpu.insn_addr = ToGuestAddr(&insn_bytes);
// Copy arg1 into store_area_
store_area_ = arg1;
SetXReg<2>(state_.cpu, ToGuestAddr(bit_cast<uint8_t*>(&store_area_)));
SetXReg<3>(state_.cpu, arg2);
InterpretInsn(&state_);
EXPECT_EQ(GetXReg<1>(state_.cpu), expected_result);
EXPECT_EQ(store_area_, expected_memory);
}
void InterpretAmo(uint32_t insn_bytes32, uint32_t insn_bytes64, uint64_t expected_memory) {
InterpretAmo(insn_bytes32, 0xffff'eeee'dddd'ccccULL, 0xaaaa'bbbb'cccc'ddddULL,
0xffff'ffff'dddd'ccccULL, 0xffff'eeee'0000'0000 | uint32_t(expected_memory));
InterpretAmo(insn_bytes64, 0xffff'eeee'dddd'ccccULL, 0xaaaa'bbbb'cccc'ddddULL,
0xffff'eeee'dddd'ccccULL, expected_memory);
}
void InterpretLui(uint32_t insn_bytes, uint64_t expected_result) {
auto code_start = ToGuestAddr(&insn_bytes);
state_.cpu.insn_addr = code_start;
InterpretInsn(&state_);
EXPECT_EQ(GetXReg<1>(state_.cpu), expected_result);
}
void InterpretAuipc(uint32_t insn_bytes, uint64_t expected_offset) {
auto code_start = ToGuestAddr(&insn_bytes);
state_.cpu.insn_addr = code_start;
InterpretInsn(&state_);
EXPECT_EQ(GetXReg<1>(state_.cpu), expected_offset + code_start);
}
void InterpretOpImm(uint32_t insn_bytes,
std::initializer_list<std::tuple<uint64_t, uint16_t, uint64_t>> args) {
for (auto [arg1, imm, expected_result] : args) {
CHECK_LE(imm, 63);
uint32_t insn_bytes_with_immediate = insn_bytes | imm << 20;
state_.cpu.insn_addr = bit_cast<GuestAddr>(&insn_bytes_with_immediate);
SetXReg<2>(state_.cpu, arg1);
InterpretInsn(&state_);
EXPECT_EQ(GetXReg<1>(state_.cpu), expected_result);
}
}
void InterpretLoad(uint32_t insn_bytes, uint64_t expected_result) {
state_.cpu.insn_addr = ToGuestAddr(&insn_bytes);
// Offset is always 8.
SetXReg<2>(state_.cpu, ToGuestAddr(bit_cast<uint8_t*>(&kDataToLoad) - 8));
InterpretInsn(&state_);
EXPECT_EQ(GetXReg<1>(state_.cpu), expected_result);
}
void InterpretLoadFp(uint32_t insn_bytes, uint64_t expected_result) {
state_.cpu.insn_addr = ToGuestAddr(&insn_bytes);
// Offset is always 8.
SetXReg<2>(state_.cpu, ToGuestAddr(bit_cast<uint8_t*>(&kDataToLoad) - 8));
InterpretInsn(&state_);
EXPECT_EQ(GetFReg<1>(state_.cpu), expected_result);
}
void InterpretStore(uint32_t insn_bytes, uint64_t expected_result) {
state_.cpu.insn_addr = ToGuestAddr(&insn_bytes);
// Offset is always 8.
SetXReg<1>(state_.cpu, ToGuestAddr(bit_cast<uint8_t*>(&store_area_) - 8));
SetXReg<2>(state_.cpu, kDataToStore);
store_area_ = 0;
InterpretInsn(&state_);
EXPECT_EQ(store_area_, expected_result);
}
void InterpretStoreFp(uint32_t insn_bytes, uint64_t expected_result) {
state_.cpu.insn_addr = ToGuestAddr(&insn_bytes);
// Offset is always 8.
SetXReg<1>(state_.cpu, ToGuestAddr(bit_cast<uint8_t*>(&store_area_) - 8));
SetFReg<2>(state_.cpu, kDataToStore);
store_area_ = 0;
InterpretInsn(&state_);
EXPECT_EQ(store_area_, expected_result);
}
void InterpretBranch(uint32_t insn_bytes,
std::initializer_list<std::tuple<uint64_t, uint64_t, int8_t>> args) {
auto code_start = ToGuestAddr(&insn_bytes);
for (auto [arg1, arg2, expected_offset] : args) {
state_.cpu.insn_addr = code_start;
SetXReg<1>(state_.cpu, arg1);
SetXReg<2>(state_.cpu, arg2);
InterpretInsn(&state_);
EXPECT_EQ(state_.cpu.insn_addr, code_start + expected_offset);
}
}
void InterpretJumpAndLink(uint32_t insn_bytes, int8_t expected_offset) {
auto code_start = ToGuestAddr(&insn_bytes);
state_.cpu.insn_addr = code_start;
InterpretInsn(&state_);
EXPECT_EQ(state_.cpu.insn_addr, code_start + expected_offset);
EXPECT_EQ(GetXReg<1>(state_.cpu), code_start + 4);
}
void InterpretJumpAndLinkRegister(uint32_t insn_bytes, uint64_t base_disp,
int64_t expected_offset) {
auto code_start = ToGuestAddr(&insn_bytes);
state_.cpu.insn_addr = code_start;
SetXReg<2>(state_.cpu, code_start + base_disp);
InterpretInsn(&state_);
EXPECT_EQ(state_.cpu.insn_addr, code_start + expected_offset);
}
protected:
static constexpr uint64_t kDataToLoad{0xffffeeeeddddccccULL};
static constexpr uint64_t kDataToStore = kDataToLoad;
uint64_t store_area_;
ThreadState state_;
};
TEST_F(Riscv64InterpreterTest, CFld) {
union {
uint16_t offset;
struct {
uint8_t : 3;
uint8_t i3_i5 : 3;
uint8_t i6_i7 : 2;
} i_bits;
};
for (offset = int16_t{0}; offset < int16_t{256}; offset += 8) {
union {
int16_t parcel;
struct {
uint8_t low_opcode : 2;
uint8_t rd : 3;
uint8_t i6_i7 : 2;
uint8_t rs : 3;
uint8_t i3_i5 : 3;
uint8_t high_opcode : 3;
} __attribute__((__packed__));
} o_bits = {
.low_opcode = 0b00,
.rd = 0,
.i6_i7 = i_bits.i6_i7,
.rs = 0,
.i3_i5 = i_bits.i3_i5,
.high_opcode = 0b001,
};
InterpretCFld(o_bits.parcel, offset);
}
}
TEST_F(Riscv64InterpreterTest, CAddi) {
union {
int8_t offset;
struct {
uint8_t i4_i0 : 5;
uint8_t i5 : 1;
} i_bits;
};
for (offset = int8_t{-32}; offset < int8_t{31}; offset++) {
union {
int16_t parcel;
struct {
uint8_t low_opcode : 2;
uint8_t i4_i0 : 5;
uint8_t r : 5;
uint8_t i5 : 1;
uint8_t high_opcode : 3;
} __attribute__((__packed__));
} o_bits = {
.low_opcode = 0b01,
.i4_i0 = i_bits.i4_i0,
.r = 2,
.i5 = i_bits.i5,
.high_opcode = 0b000,
};
InterpretCAddi(o_bits.parcel, offset);
}
}
TEST_F(Riscv64InterpreterTest, CAddi4spn) {
union {
int16_t offset;
struct {
uint8_t : 2;
uint8_t i2 : 1;
uint8_t i3 : 1;
uint8_t i4 : 1;
uint8_t i5 : 1;
uint8_t i6 : 1;
uint8_t i7 : 1;
uint8_t i8 : 1;
uint8_t i9 : 1;
} i_bits;
};
for (offset = int16_t{4}; offset < int16_t{1024}; offset += 4) {
union {
int16_t parcel;
struct {
uint8_t low_opcode : 2;
uint8_t rd : 3;
uint8_t i3 : 1;
uint8_t i2 : 1;
uint8_t i6 : 1;
uint8_t i7 : 1;
uint8_t i8 : 1;
uint8_t i9 : 1;
uint8_t i4 : 1;
uint8_t i5 : 1;
uint8_t high_opcode : 3;
};
} o_bits = {
.low_opcode = 0b00,
.rd = 1,
.i3 = i_bits.i3,
.i2 = i_bits.i2,
.i6 = i_bits.i6,
.i7 = i_bits.i7,
.i8 = i_bits.i8,
.i9 = i_bits.i9,
.i4 = i_bits.i4,
.i5 = i_bits.i5,
.high_opcode = 0b000,
};
InterpretCAddi4spn(o_bits.parcel, offset);
}
}
TEST_F(Riscv64InterpreterTest, CJ) {
union {
int16_t offset;
struct {
uint8_t : 1;
uint8_t i1 : 1;
uint8_t i2 : 1;
uint8_t i3 : 1;
uint8_t i4 : 1;
uint8_t i5 : 1;
uint8_t i6 : 1;
uint8_t i7 : 1;
uint8_t i8 : 1;
uint8_t i9 : 1;
uint8_t i10 : 1;
uint8_t i11 : 1;
} i_bits;
};
for (offset = int16_t{-2048}; offset < int16_t{2048}; offset += 2) {
union {
int16_t parcel;
struct {
uint8_t low_opcode : 2;
uint8_t i5 : 1;
uint8_t i1 : 1;
uint8_t i2 : 1;
uint8_t i3 : 1;
uint8_t i7 : 1;
uint8_t i6 : 1;
uint8_t i10 : 1;
uint8_t i8 : 1;
uint8_t i9 : 1;
uint8_t i4 : 1;
uint8_t i11 : 1;
uint8_t high_opcode : 3;
};
} o_bits = {
.low_opcode = 0b01,
.i5 = i_bits.i5,
.i1 = i_bits.i1,
.i2 = i_bits.i2,
.i3 = i_bits.i3,
.i7 = i_bits.i7,
.i6 = i_bits.i6,
.i10 = i_bits.i10,
.i8 = i_bits.i8,
.i9 = i_bits.i9,
.i4 = i_bits.i4,
.i11 = i_bits.i11,
.high_opcode = 0b101,
};
InterpretCJ(o_bits.parcel, offset);
}
}
TEST_F(Riscv64InterpreterTest, FenceInstructions) {
// Fence
InterpretFence(0x0ff0000f);
// FenceTso
InterpretFence(0x8330000f);
// FenceI
InterpretFence(0x0000100f);
}
TEST_F(Riscv64InterpreterTest, OpInstructions) {
// Add
InterpretOp(0x003100b3, {{19, 23, 42}});
// Sub
InterpretOp(0x403100b3, {{42, 23, 19}});
// And
InterpretOp(0x003170b3, {{0b0101, 0b0011, 0b0001}});
// Or
InterpretOp(0x003160b3, {{0b0101, 0b0011, 0b0111}});
// Xor
InterpretOp(0x003140b3, {{0b0101, 0b0011, 0b0110}});
// Sll
InterpretOp(0x003110b3, {{0b1010, 3, 0b1010'000}});
// Srl
InterpretOp(0x003150b3, {{0xf000'0000'0000'0000ULL, 12, 0x000f'0000'0000'0000ULL}});
// Sra
InterpretOp(0x403150b3, {{0xf000'0000'0000'0000ULL, 12, 0xffff'0000'0000'0000ULL}});
// Slt
InterpretOp(0x003120b3, {
{19, 23, 1},
{23, 19, 0},
{~0ULL, 0, 1},
});
// Sltu
InterpretOp(0x003130b3, {
{19, 23, 1},
{23, 19, 0},
{~0ULL, 0, 0},
});
// Mul
InterpretOp(0x023100b3, {{0x9999'9999'9999'9999, 0x9999'9999'9999'9999, 0x0a3d'70a3'd70a'3d71}});
// Mulh
InterpretOp(0x23110b3, {{0x9999'9999'9999'9999, 0x9999'9999'9999'9999, 0x28f5'c28f'5c28'f5c3}});
// Mulhsu
InterpretOp(0x23120b3, {{0x9999'9999'9999'9999, 0x9999'9999'9999'9999, 0xc28f'5c28'f5c2'8f5c}});
// Mulhu
InterpretOp(0x23130b3, {{0x9999'9999'9999'9999, 0x9999'9999'9999'9999, 0x5c28'f5c2'8f5c'28f5}});
// Div
InterpretOp(0x23140b3, {{0x9999'9999'9999'9999, 0x3333, 0xfffd'fffd'fffd'fffe}});
// Div
InterpretOp(0x23150b3, {{0x9999'9999'9999'9999, 0x3333, 0x0003'0003'0003'0003}});
// Rem
InterpretOp(0x23160b3, {{0x9999'9999'9999'9999, 0x3333, 0xffff'ffff'ffff'ffff}});
// Remu
InterpretOp(0x23170b3, {{0x9999'9999'9999'9999, 0x3333, 0}});
}
TEST_F(Riscv64InterpreterTest, Op32Instructions) {
// Addw
InterpretOp(0x003100bb, {{19, 23, 42}});
// Subw
InterpretOp(0x403100bb, {{42, 23, 19}});
// Sllw
InterpretOp(0x003110bb, {{0b1010, 3, 0b1010'000}});
// Srlw
InterpretOp(0x003150bb, {{0x0000'0000'f000'0000ULL, 12, 0x0000'0000'000f'0000ULL}});
// Sraw
InterpretOp(0x403150bb, {{0x0000'0000'f000'0000ULL, 12, 0xffff'ffff'ffff'0000ULL}});
// Mulw
InterpretOp(0x023100bb, {{0x9999'9999'9999'9999, 0x9999'9999'9999'9999, 0xffff'ffff'd70a'3d71}});
// Divw
InterpretOp(0x23140bb, {{0x9999'9999'9999'9999, 0x3333, 0xffff'ffff'fffd'fffe}});
// Divuw
InterpretOp(0x23150bb, {{0x9999'9999'9999'9999, 0x3333, 0x0000'0000'0003'0003}});
// Remw
InterpretOp(0x23160bb, {{0x9999'9999'9999'9999, 0x3333, 0xffff'ffff'ffff'ffff}});
// Remuw
InterpretOp(0x23170bb, {{0x9999'9999'9999'9999, 0x3333, 0}});
}
TEST_F(Riscv64InterpreterTest, AmoInstructions) {
// Verifying that all aq and rl combinations work for Amoswap, but only test relaxed one for most
// other instructions for brevity.
// AmoswaoW/AmoswaoD
InterpretAmo(0x083120af, 0x083130af, 0xaaaa'bbbb'cccc'ddddULL);
// AmoswapWAq/AmoswapDAq
InterpretAmo(0x0c3120af, 0x0c3130af, 0xaaaa'bbbb'cccc'ddddULL);
// AmoswapWRl/AmoswapDRl
InterpretAmo(0x0a3120af, 0x0a3130af, 0xaaaa'bbbb'cccc'ddddULL);
// AmoswapWAqrl/AmoswapDAqrl
InterpretAmo(0x0e3120af, 0x0e3130af, 0xaaaa'bbbb'cccc'ddddULL);
// AmoaddW/AmoaddD
InterpretAmo(0x003120af, 0x003130af, 0xaaaa'aaaa'aaaa'aaa9);
// AmoxorW/AmoxorD
InterpretAmo(0x203120af, 0x203130af, 0x5555'5555'1111'1111);
// AmoandW/AmoandD
InterpretAmo(0x603120af, 0x603130af, 0xaaaa'aaaa'cccc'cccc);
// AmoorW/AmoorD
InterpretAmo(0x403120af, 0x403130af, 0xffff'ffff'dddd'dddd);
// AmominW/AmominD
InterpretAmo(0x803120af, 0x803130af, 0xaaaa'bbbb'cccc'ddddULL);
// AmomaxW/AmomaxD
InterpretAmo(0xa03120af, 0xa03130af, 0xffff'eeee'dddd'ccccULL);
// AmominuW/AmominuD
InterpretAmo(0xc03120af, 0xc03130af, 0xaaaa'bbbb'cccc'ddddULL);
// AmomaxuW/AmomaxuD
InterpretAmo(0xe03120af, 0xe03130af, 0xffff'eeee'dddd'ccccULL);
}
TEST_F(Riscv64InterpreterTest, UpperImmArgs) {
// Lui
InterpretLui(0xfedcb0b7, 0xffff'ffff'fedc'b000);
// Auipc
InterpretAuipc(0xfedcb097, 0xffff'ffff'fedc'b000);
}
TEST_F(Riscv64InterpreterTest, OpImmInstructions) {
// Addi
InterpretOpImm(0x00010093, {{19, 23, 42}});
// Slli
InterpretOpImm(0x00011093, {{0b1010, 3, 0b1010'000}});
// Slti
InterpretOpImm(0x00012093, {
{19, 23, 1},
{23, 19, 0},
{~0ULL, 0, 1},
});
// Sltiu
InterpretOpImm(0x00013093, {
{19, 23, 1},
{23, 19, 0},
{~0ULL, 0, 0},
});
// Xori
InterpretOpImm(0x00014093, {{0b0101, 0b0011, 0b0110}});
// Srli
InterpretOpImm(0x00015093, {{0xf000'0000'0000'0000ULL, 12, 0x000f'0000'0000'0000ULL}});
// Srai
InterpretOpImm(0x40015093, {{0xf000'0000'0000'0000ULL, 12, 0xffff'0000'0000'0000ULL}});
// Ori
InterpretOpImm(0x00016093, {{0b0101, 0b0011, 0b0111}});
// Andi
InterpretOpImm(0x00017093, {{0b0101, 0b0011, 0b0001}});
}
TEST_F(Riscv64InterpreterTest, OpImm32Instructions) {
// Addiw
InterpretOpImm(0x0001009b, {{19, 23, 42}});
// Slliw
InterpretOpImm(0x0001109b, {{0b1010, 3, 0b1010'000}});
// Srliw
InterpretOpImm(0x0001509b, {{0x0000'0000'f000'0000ULL, 12, 0x0000'0000'000f'0000ULL}});
// Sraiw
InterpretOpImm(0x4001509b, {{0x0000'0000'f000'0000ULL, 12, 0xffff'ffff'ffff'0000ULL}});
}
TEST_F(Riscv64InterpreterTest, LoadInstructions) {
// Offset is always 8.
// Lbu
InterpretLoad(0x00814083, kDataToLoad & 0xffULL);
// Lhu
InterpretLoad(0x00815083, kDataToLoad & 0xffffULL);
// Lwu
InterpretLoad(0x00816083, kDataToLoad & 0xffff'ffffULL);
// Ldu
InterpretLoad(0x00813083, kDataToLoad);
// Lb
InterpretLoad(0x00810083, int64_t{int8_t(kDataToLoad)});
// Lh
InterpretLoad(0x00811083, int64_t{int16_t(kDataToLoad)});
// Lw
InterpretLoad(0x00812083, int64_t{int32_t(kDataToLoad)});
}
TEST_F(Riscv64InterpreterTest, LoadFpInstructions) {
// Offset is always 8.
// Flw
InterpretLoadFp(0x00812087, kDataToLoad | 0xffffffff00000000ULL);
// Fld
InterpretLoadFp(0x00813087, kDataToLoad);
}
TEST_F(Riscv64InterpreterTest, StoreInstructions) {
// Offset is always 8.
// Sb
InterpretStore(0x00208423, kDataToStore & 0xffULL);
// Sh
InterpretStore(0x00209423, kDataToStore & 0xffffULL);
// Sw
InterpretStore(0x0020a423, kDataToStore & 0xffff'ffffULL);
// Sd
InterpretStore(0x0020b423, kDataToStore);
}
TEST_F(Riscv64InterpreterTest, StoreFpInstructions) {
// Offset is always 8.
// Fsw
InterpretStoreFp(0x0020a427, kDataToStore & 0xffff'ffffULL);
// Fsd
InterpretStoreFp(0x0020b427, kDataToStore);
}
TEST_F(Riscv64InterpreterTest, BranchInstructions) {
// Beq
InterpretBranch(0x00208463, {
{42, 42, 8},
{41, 42, 4},
{42, 41, 4},
});
// Bne
InterpretBranch(0x00209463, {
{42, 42, 4},
{41, 42, 8},
{42, 41, 8},
});
// Blt
InterpretBranch(0x0020c463, {
{41, 42, 8},
{42, 42, 4},
{42, 41, 4},
{0xf000'0000'0000'0000ULL, 42, 8},
{42, 0xf000'0000'0000'0000ULL, 4},
});
// Bltu
InterpretBranch(0x0020e463, {
{41, 42, 8},
{42, 42, 4},
{42, 41, 4},
{0xf000'0000'0000'0000ULL, 42, 4},
{42, 0xf000'0000'0000'0000ULL, 8},
});
// Bge
InterpretBranch(0x0020d463, {
{42, 41, 8},
{42, 42, 8},
{41, 42, 4},
{0xf000'0000'0000'0000ULL, 42, 4},
{42, 0xf000'0000'0000'0000ULL, 8},
});
// Bgeu
InterpretBranch(0x0020f463, {
{42, 41, 8},
{42, 42, 8},
{41, 42, 4},
{0xf000'0000'0000'0000ULL, 42, 8},
{42, 0xf000'0000'0000'0000ULL, 4},
});
// Beq with negative offset.
InterpretBranch(0xfe208ee3, {
{42, 42, -4},
});
}
TEST_F(Riscv64InterpreterTest, JumpAndLinkInstructions) {
// Jal
InterpretJumpAndLink(0x008000ef, 8);
// Jal with negative offset.
InterpretJumpAndLink(0xffdff0ef, -4);
}
TEST_F(Riscv64InterpreterTest, JumpAndLinkRegisterInstructions) {
// Jalr offset=4.
InterpretJumpAndLinkRegister(0x004100e7, 38, 42);
// Jalr offset=-4.
InterpretJumpAndLinkRegister(0xffc100e7, 42, 38);
// Jalr offset=5 - must properly align the target to even.
InterpretJumpAndLinkRegister(0x005100e7, 38, 42);
}
TEST_F(Riscv64InterpreterTest, SyscallWrite) {
const char message[] = "Hello";
// Prepare a pipe to write to.
int pipefd[2];
ASSERT_EQ(0, pipe(pipefd));
// SYS_write
SetXReg<17>(state_.cpu, 0x40);
// File descriptor
SetXReg<10>(state_.cpu, pipefd[1]);
// String
SetXReg<11>(state_.cpu, bit_cast<uint64_t>(&message[0]));
// Size
SetXReg<12>(state_.cpu, sizeof(message));
uint32_t insn_bytes = 0x00000073;
state_.cpu.insn_addr = ToGuestAddr(&insn_bytes);
InterpretInsn(&state_);
// Check number of bytes written.
EXPECT_EQ(GetXReg<10>(state_.cpu), sizeof(message));
// Check the message was written to the pipe.
char buf[sizeof(message)] = {};
read(pipefd[0], &buf, sizeof(buf));
EXPECT_EQ(0, strcmp(message, buf));
close(pipefd[0]);
close(pipefd[1]);
}
} // namespace
} // namespace berberis