aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base/Android.bp11
-rw-r--r--base/exec_region_elf_backed.cc67
-rw-r--r--base/exec_region_elf_backed_test.cc71
-rw-r--r--decoder/include/berberis/decoder/riscv64/decoder.h64
-rw-r--r--decoder/include/berberis/decoder/riscv64/semantics_player.h13
-rw-r--r--exec_region/Android.bp27
-rw-r--r--exec_region/exec_region.cc15
-rw-r--r--exec_region/sections.ld55
-rw-r--r--guest_state/include/berberis/guest_state/guest_state_riscv64.h15
-rw-r--r--interpreter/Android.bp2
-rw-r--r--interpreter/riscv64/interpreter.cc43
-rw-r--r--interpreter/riscv64/interpreter_test.cc20
-rw-r--r--intrinsics/Android.bp7
-rw-r--r--intrinsics/include/berberis/intrinsics/guest_fpstate.h14
-rw-r--r--intrinsics/include/berberis/intrinsics/riscv64/guest_fpstate.h58
-rw-r--r--kernel_api/fcntl_emulation.cc66
16 files changed, 539 insertions, 9 deletions
diff --git a/base/Android.bp b/base/Android.bp
index ea775e28..b07c5d40 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -48,6 +48,12 @@ cc_library_static {
srcs: ["raw_syscall_x86_64.S"],
},
},
+ target: {
+ bionic: {
+ srcs: ["exec_region_elf_backed.cc"],
+ },
+ },
+
header_libs: ["libberberis_base_headers"],
export_header_lib_headers: ["libberberis_base_headers"],
}
@@ -78,5 +84,10 @@ cc_test_library {
"mmap_pool_test.cc",
"pointer_and_counter_test.cc",
],
+ target: {
+ bionic: {
+ srcs: ["exec_region_elf_backed_test.cc"],
+ },
+ },
header_libs: ["libberberis_base_headers"],
}
diff --git a/base/exec_region_elf_backed.cc b/base/exec_region_elf_backed.cc
new file mode 100644
index 00000000..aa272c28
--- /dev/null
+++ b/base/exec_region_elf_backed.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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 "berberis/base/exec_region_elf_backed.h"
+
+#include <android/dlext.h>
+#include <dlfcn.h>
+#include <sys/mman.h>
+
+#include "berberis/base/bit_util.h"
+#include "berberis/base/mmap.h"
+
+// Note that we have to use absolute path for ANDROID_DLEXT_FORCE_LOAD to work correctly
+// otherwise searching by soname will trigger and the flag will have no effect.
+#if defined(__LP64__)
+const constexpr char* kExecRegionLibraryPath = "/system/lib64/libberberis_exec_region.so";
+#else
+const constexpr char* kExecRegionLibraryPath = "/system/lib/libberberis_exec_region.so";
+#endif
+
+const constexpr char* kRegionStartSymbolName = "exec_region_start";
+const constexpr char* kRegionEndSymbolName = "exec_region_end";
+
+namespace berberis {
+
+ExecRegion ExecRegionElfBackedFactory::Create(size_t size) {
+ size = AlignUpPageSize(size);
+
+ android_dlextinfo dlextinfo{.flags = ANDROID_DLEXT_FORCE_LOAD};
+ void* handle = android_dlopen_ext(kExecRegionLibraryPath, RTLD_NOW, &dlextinfo);
+ if (handle == nullptr) {
+ FATAL("Couldn't load \"%s\": %s", kExecRegionLibraryPath, dlerror());
+ }
+ void* region_start = dlsym(handle, kRegionStartSymbolName);
+ CHECK(region_start != nullptr);
+ auto region_start_addr = bit_cast<uintptr_t>(region_start);
+ CHECK(region_start_addr % kPageSize == 0);
+
+ void* region_end = dlsym(handle, kRegionEndSymbolName);
+ CHECK(region_end != nullptr);
+ auto region_end_addr = bit_cast<uintptr_t>(region_end);
+ CHECK(region_end_addr % kPageSize == 0);
+ size_t region_size = region_end_addr - region_start_addr;
+ CHECK_GE(region_size, size);
+
+ return ExecRegion{
+ static_cast<uint8_t*>(MmapImplOrDie({.addr = region_start,
+ .size = region_size,
+ .prot = PROT_READ | PROT_WRITE | PROT_EXEC,
+ .flags = MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS})),
+ region_size};
+}
+
+} // namespace berberis
diff --git a/base/exec_region_elf_backed_test.cc b/base/exec_region_elf_backed_test.cc
new file mode 100644
index 00000000..3eeb4570
--- /dev/null
+++ b/base/exec_region_elf_backed_test.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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 "berberis/base/exec_region_elf_backed.h"
+
+#include <dlfcn.h>
+
+namespace berberis {
+
+namespace {
+
+TEST(ExecRegionElfBacked, Smoke) {
+ const char buf[] = "deadbeef";
+
+ ExecRegion exec = ExecRegionElfBackedFactory::Create(sizeof(buf));
+ const uint8_t* code = exec.begin();
+ ASSERT_NE(nullptr, code);
+
+ exec.Write(code, buf, sizeof(buf));
+ ASSERT_EQ('f', code[7]);
+
+ exec.Detach();
+ ASSERT_EQ('f', code[7]);
+
+ exec.Free();
+}
+
+TEST(ExecRegionElfBacked, PltIsExecutable_b_254823538) {
+ // DlClose calls .plt section for __cxa_finalize
+ // This test makes sure it is called without incidents
+ // http://b/254823538
+ void* handle = dlopen("libberberis_exec_region.so", RTLD_NOW);
+ EXPECT_NE(handle, nullptr) << dlerror();
+ dlclose(handle);
+}
+
+TEST(ExecRegionElfBacked, TwoRegionsHaveDifferentAddresses) {
+ auto region1 = ExecRegionElfBackedFactory::Create(1);
+ auto region2 = ExecRegionElfBackedFactory::Create(1);
+ EXPECT_NE(region1.begin(), region2.begin());
+ region1.Free();
+ region2.Free();
+}
+
+TEST(ExecRegionElfBacked, RegionOfDifferentSizes) {
+ auto region = ExecRegionElfBackedFactory::Create(ExecRegionElfBackedFactory::kExecRegionSize);
+ region.Free();
+ // Anything bigger should result it CHECK fail.
+ EXPECT_DEATH(
+ (void)ExecRegionElfBackedFactory::Create(ExecRegionElfBackedFactory::kExecRegionSize + 1),
+ "");
+}
+
+} // namespace
+
+} // namespace berberis
diff --git a/decoder/include/berberis/decoder/riscv64/decoder.h b/decoder/include/berberis/decoder/riscv64/decoder.h
index 0e75d425..fd1389a4 100644
--- a/decoder/include/berberis/decoder/riscv64/decoder.h
+++ b/decoder/include/berberis/decoder/riscv64/decoder.h
@@ -112,6 +112,20 @@ class Decoder {
kMaxCompressedOpcode = 0b111'11,
};
+ enum class CsrOpcode {
+ kCsrrw = 0b01,
+ kCsrrs = 0b10,
+ kCsrrc = 0b11,
+ kMaxCsrOpcode = 0b11,
+ };
+
+ enum class CsrImmOpcode {
+ kCsrrwi = 0b01,
+ kCsrrsi = 0b10,
+ kCsrrci = 0b11,
+ kMaxCsrOpcode = 0b11,
+ };
+
enum class FenceOpcode {
kFence = 0b0000,
kFenceTso = 0b1000,
@@ -256,6 +270,13 @@ class Decoder {
kMaxBranchOpcode = 0b111,
};
+ enum class CsrRegister {
+ kFFlags = 0b00'00'0000'0001,
+ kFrm = 0b00'00'0000'0010,
+ kFCsr = 0b00'00'0000'0011,
+ kMaxCsrRegister = 0b11'11'1111'1111,
+ };
+
struct AmoArgs {
AmoOpcode opcode;
uint8_t dst;
@@ -265,6 +286,20 @@ class Decoder {
bool aq : 1;
};
+ struct CsrArgs {
+ CsrOpcode opcode;
+ uint8_t dst;
+ uint8_t src;
+ CsrRegister csr;
+ };
+
+ struct CsrImmArgs {
+ CsrImmOpcode opcode;
+ uint8_t dst;
+ uint8_t imm;
+ CsrRegister csr;
+ };
+
struct FenceArgs {
FenceOpcode opcode;
uint8_t dst;
@@ -770,11 +805,32 @@ class Decoder {
}
void DecodeSystem() {
- int32_t opcode = GetBits<uint32_t, 7, 25>();
- const SystemArgs args = {
- .opcode = SystemOpcode(opcode),
+ uint8_t low_opcode = GetBits<uint8_t, 12, 2>();
+ if (low_opcode == 0b00) {
+ int32_t opcode = GetBits<uint32_t, 7, 25>();
+ const SystemArgs args = {
+ .opcode = SystemOpcode(opcode),
+ };
+ return insn_consumer_->System(args);
+ }
+ if (GetBits<uint8_t, 14, 1>()) {
+ CsrImmOpcode opcode = CsrImmOpcode(low_opcode);
+ const CsrImmArgs args = {
+ .opcode = opcode,
+ .dst = GetBits<uint8_t, 7, 5>(),
+ .imm = GetBits<uint8_t, 15, 5>(),
+ .csr = CsrRegister(GetBits<uint16_t, 20, 12>()),
+ };
+ return insn_consumer_->Csr(args);
+ }
+ CsrOpcode opcode = CsrOpcode(low_opcode);
+ const CsrArgs args = {
+ .opcode = opcode,
+ .dst = GetBits<uint8_t, 7, 5>(),
+ .src = GetBits<uint8_t, 15, 5>(),
+ .csr = CsrRegister(GetBits<uint16_t, 20, 12>()),
};
- insn_consumer_->System(args);
+ return insn_consumer_->Csr(args);
}
void DecodeJumpAndLinkRegister() {
diff --git a/decoder/include/berberis/decoder/riscv64/semantics_player.h b/decoder/include/berberis/decoder/riscv64/semantics_player.h
index 683b6dea..40b559d3 100644
--- a/decoder/include/berberis/decoder/riscv64/semantics_player.h
+++ b/decoder/include/berberis/decoder/riscv64/semantics_player.h
@@ -35,6 +35,19 @@ class SemanticsPlayer {
// Decoder's InsnConsumer implementation.
+ void Csr(const typename Decoder::CsrArgs& args) {
+ Register result;
+ Register arg = GetRegOrZero(args.src);
+ result = listener_->Csr(args.opcode, arg, args.csr);
+ SetRegOrIgnore(args.dst, result);
+ }
+
+ void Csr(const typename Decoder::CsrImmArgs& args) {
+ Register result;
+ result = listener_->Csr(args.opcode, args.imm, args.csr);
+ SetRegOrIgnore(args.dst, result);
+ }
+
void Fence(const typename Decoder::FenceArgs& args) {
listener_->Fence(args.opcode,
args.src,
diff --git a/exec_region/Android.bp b/exec_region/Android.bp
new file mode 100644
index 00000000..d88326e0
--- /dev/null
+++ b/exec_region/Android.bp
@@ -0,0 +1,27 @@
+// 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.
+//
+
+package {
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ ],
+}
+
+cc_library_shared {
+ name: "libberberis_exec_region",
+ defaults: ["berberis_defaults"],
+ srcs: ["exec_region.cc"],
+ linker_scripts: ["sections.ld"],
+}
diff --git a/exec_region/exec_region.cc b/exec_region/exec_region.cc
new file mode 100644
index 00000000..2c874b48
--- /dev/null
+++ b/exec_region/exec_region.cc
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2022 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.
+ */
diff --git a/exec_region/sections.ld b/exec_region/sections.ld
new file mode 100644
index 00000000..097003c2
--- /dev/null
+++ b/exec_region/sections.ld
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+SECTIONS {
+ . = SIZEOF_HEADERS;
+ .text : {
+ *(.text.*)
+ . = ALIGN(4096);
+ exec_region_start = .;
+ . += (512 * 1024);
+ . = ALIGN(4096);
+ exec_region_end = .;
+ }
+ .plt : {
+ *(.plt)
+ }
+ /* Align following segments on the page boundary to prevent
+ * next PT_LOAD segment from mapping over .plt section removing
+ * executable flag from .plt. See also http://b/254823538.
+ */
+ . = ALIGN(4096);
+ .fini_array : {
+ *(.fini_array.*)
+ }
+ .init_array : {
+ *(.ini_array.*)
+ }
+ .dynamic : {
+ *(.dynamic)
+ }
+ .got : {
+ *(.got)
+ }
+ .got.plt : {
+ *(.got.plt)
+ }
+ /* Align the rest of segments on the page boundary to prevent
+ * GNU_RELRO segment from mprotecting writable flag away
+ * from them. See also http://b/261807330.
+ */
+ . = ALIGN(4096);
+}
diff --git a/guest_state/include/berberis/guest_state/guest_state_riscv64.h b/guest_state/include/berberis/guest_state/guest_state_riscv64.h
index ff6a1b40..8546311f 100644
--- a/guest_state/include/berberis/guest_state/guest_state_riscv64.h
+++ b/guest_state/include/berberis/guest_state/guest_state_riscv64.h
@@ -30,6 +30,21 @@ struct CPUState {
// f0 to f31. We are using uint64_t because C++ may change values of NaN when they are passed from
// or to function and RISC-V uses NaN-boxing which would make things problematic.
uint64_t f[32];
+ // RISC-V has five rounding modes, while x86-64 has only four.
+ //
+ // Extra rounding mode (RMM in RISC-V documentation) is emulated but requires the use of
+ // FE_TOWARDZERO mode for correct work.
+ //
+ // Additionally RISC-V implementation is supposed to support three “illegal” rounding modes and
+ // when they are selected all instructions which use rounding mode trigger “undefined instruction”
+ // exception.
+ //
+ // For simplicity we always keep full rounding mode (3 bits) in the frm field and set host
+ // rounding mode to appropriate one.
+ //
+ // Exceptions, on the other hand, couldn't be stored here efficiently, instead we rely on the fact
+ // that x86-64 implements all five exceptions that RISC-V needs (and more).
+ uint8_t frm : 3;
GuestAddr insn_addr;
};
diff --git a/interpreter/Android.bp b/interpreter/Android.bp
index 32ac6435..37a87df3 100644
--- a/interpreter/Android.bp
+++ b/interpreter/Android.bp
@@ -33,6 +33,7 @@ cc_library_static {
"libberberis_decoder_riscv64_headers",
"libberberis_guest_state_headers",
"libberberis_interpreter_riscv64_headers",
+ "libberberis_intrinsics_headers",
"libberberis_kernel_api_headers",
],
export_header_lib_headers: ["libberberis_interpreter_riscv64_headers"],
@@ -47,6 +48,7 @@ cc_test_library {
"libberberis_base_headers",
"libberberis_guest_state_headers",
"libberberis_interpreter_riscv64_headers",
+ "libberberis_intrinsics_headers",
"libberberis_kernel_api_headers",
],
}
diff --git a/interpreter/riscv64/interpreter.cc b/interpreter/riscv64/interpreter.cc
index 63b55f15..f4e82fea 100644
--- a/interpreter/riscv64/interpreter.cc
+++ b/interpreter/riscv64/interpreter.cc
@@ -16,6 +16,7 @@
#include "berberis/interpreter/riscv64/interpreter.h"
+#include <cfenv>
#include <cstdint>
#include <cstring>
@@ -27,6 +28,7 @@
#include "berberis/decoder/riscv64/semantics_player.h"
#include "berberis/guest_state/guest_addr.h"
#include "berberis/guest_state/guest_state_riscv64.h"
+#include "berberis/intrinsics/riscv64/guest_fpstate.h"
#include "berberis/kernel_api/run_guest_syscall.h"
#include "atomics.h"
@@ -47,6 +49,47 @@ class Interpreter {
// Instruction implementations.
//
+ Register Csr(Decoder::CsrOpcode opcode, Register arg, Decoder::CsrRegister csr) {
+ Register (*UpdateStatus)(Register arg, Register original_csr_value);
+ switch (opcode) {
+ case Decoder::CsrOpcode::kCsrrw:
+ UpdateStatus = [](Register arg, Register /*original_csr_value*/) { return arg; };
+ break;
+ case Decoder::CsrOpcode::kCsrrs:
+ UpdateStatus = [](Register arg, Register original_csr_value) {
+ return arg | original_csr_value;
+ };
+ break;
+ case Decoder::CsrOpcode::kCsrrc:
+ UpdateStatus = [](Register arg, Register original_csr_value) {
+ return ~arg & original_csr_value;
+ };
+ break;
+ default:
+ Unimplemented();
+ return {};
+ }
+ Register result;
+ switch (csr) {
+ case Decoder::CsrRegister::kFrm:
+ result = state_->cpu.frm;
+ arg = UpdateStatus(arg, result);
+ state_->cpu.frm = arg;
+ if (arg <= FPFlags::RM_MAX) {
+ std::fesetround(intrinsics::ToHostRoundingMode(arg));
+ }
+ break;
+ default:
+ Unimplemented();
+ return {};
+ }
+ return result;
+ }
+
+ Register Csr(Decoder::CsrImmOpcode opcode, uint8_t imm, Decoder::CsrRegister csr) {
+ return Csr(Decoder::CsrOpcode(opcode), imm, csr);
+ }
+
// Note: we prefer not to use C11/C++ atomic_thread_fence or even gcc/clang builtin
// __atomic_thread_fence because all these function rely on the fact that compiler never uses
// non-temporal loads and stores and only issue “mfence” when sequentially consistent ordering is
diff --git a/interpreter/riscv64/interpreter_test.cc b/interpreter/riscv64/interpreter_test.cc
index e041fdab..1e4034e2 100644
--- a/interpreter/riscv64/interpreter_test.cc
+++ b/interpreter/riscv64/interpreter_test.cc
@@ -27,6 +27,7 @@
#include "berberis/guest_state/guest_addr.h"
#include "berberis/guest_state/guest_state_riscv64.h"
#include "berberis/interpreter/riscv64/interpreter.h"
+#include "berberis/intrinsics/guest_fpstate.h"
namespace berberis {
@@ -73,6 +74,15 @@ class Riscv64InterpreterTest : public ::testing::Test {
EXPECT_EQ(state_.cpu.insn_addr, code_start + expected_offset);
}
+ void InterpretCsr(uint32_t insn_bytes, uint8_t expected_rm) {
+ auto code_start = ToGuestAddr(&insn_bytes);
+ state_.cpu.insn_addr = code_start;
+ state_.cpu.frm = 0b001u;
+ InterpretInsn(&state_);
+ EXPECT_EQ(GetXReg<2>(state_.cpu), 0b001u);
+ EXPECT_EQ(state_.cpu.frm, expected_rm);
+ }
+
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) {
@@ -405,6 +415,16 @@ TEST_F(Riscv64InterpreterTest, CJ) {
}
}
+TEST_F(Riscv64InterpreterTest, CsrInstrctuion) {
+ ScopedRoundingMode scoped_rounding_mode;
+ // Csrrw x2, frm, 2
+ InterpretCsr(0x00215173, 2);
+ // Csrrsi x2, frm, 2
+ InterpretCsr(0x00216173, 3);
+ // Csrrci x2, frm, 1
+ InterpretCsr(0x0020f173, 0);
+}
+
TEST_F(Riscv64InterpreterTest, FenceInstructions) {
// Fence
InterpretFence(0x0ff0000f);
diff --git a/intrinsics/Android.bp b/intrinsics/Android.bp
index 44220fe0..9e6a6cc0 100644
--- a/intrinsics/Android.bp
+++ b/intrinsics/Android.bp
@@ -17,6 +17,13 @@ package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
+cc_library_headers {
+ name: "libberberis_intrinsics_headers",
+ defaults: ["berberis_defaults"],
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
cc_library_static {
name: "libberberis_intrinsics",
defaults: ["berberis_defaults"],
diff --git a/intrinsics/include/berberis/intrinsics/guest_fpstate.h b/intrinsics/include/berberis/intrinsics/guest_fpstate.h
index da07e470..23cce1be 100644
--- a/intrinsics/include/berberis/intrinsics/guest_fpstate.h
+++ b/intrinsics/include/berberis/intrinsics/guest_fpstate.h
@@ -21,8 +21,8 @@
// portion that is reflected in hosts' fp-environment.
// TODO(levarum): Rename file to reflect this.
-#include <fenv.h> // FE_TONEAREST and friends.
-#include <stdint.h>
+#include <cfenv> // FE_TONEAREST and friends.
+#include <cstdint>
namespace berberis {
@@ -39,6 +39,16 @@ static_assert(FE_TIESAWAY != FE_UPWARD);
static_assert(FE_TIESAWAY != FE_DOWNWARD);
static_assert(FE_TIESAWAY != FE_TOWARDZERO);
+class ScopedRoundingMode {
+ public:
+ ScopedRoundingMode() : saved_round_mode(std::fegetround()) {}
+ ScopedRoundingMode(int rm) : saved_round_mode(std::fegetround()) { std::fesetround(rm); }
+ ~ScopedRoundingMode() { std::fesetround(saved_round_mode); }
+
+ private:
+ int saved_round_mode;
+};
+
} // namespace berberis
#endif // BERBERIS_INTRINSICS_GUEST_FPSTATE_H_
diff --git a/intrinsics/include/berberis/intrinsics/riscv64/guest_fpstate.h b/intrinsics/include/berberis/intrinsics/riscv64/guest_fpstate.h
new file mode 100644
index 00000000..e8a68941
--- /dev/null
+++ b/intrinsics/include/berberis/intrinsics/riscv64/guest_fpstate.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef BERBERIS_INTRINSICS_GUEST_RISCV64_FPSTATE_H_
+#define BERBERIS_INTRINSICS_GUEST_RISCV64_FPSTATE_H_
+
+#include <cfenv>
+#include <cstdint>
+
+namespace berberis {
+
+namespace FPFlags {
+
+inline constexpr uint64_t NV = 1 << 4;
+inline constexpr uint64_t DZ = 1 << 3;
+inline constexpr uint64_t OF = 1 << 2;
+inline constexpr uint64_t UF = 1 << 1;
+inline constexpr uint64_t NX = 1 << 0;
+inline constexpr uint64_t RM_POS = 5;
+inline constexpr uint64_t RM_MASK = 0b111;
+inline constexpr uint64_t RM_MAX = 0b100;
+inline constexpr uint64_t RNE = 0b000;
+inline constexpr uint64_t RTZ = 0b001;
+inline constexpr uint64_t RDN = 0b010;
+inline constexpr uint64_t RUP = 0b011;
+inline constexpr uint64_t RMM = 0b100;
+inline constexpr uint64_t DYN = 0b111;
+
+} // namespace FPFlags
+
+namespace intrinsics {
+
+// Note that not all RISC-V rounding modes are supported on popular architectures.
+// FE_TIESAWAY is emulated, but proper emulation needs FE_TOWARDZERO mode.
+inline int ToHostRoundingMode(int8_t rm) {
+ static constexpr int kRounding[FPFlags::RM_MAX + 1] = {
+ FE_TONEAREST, FE_TOWARDZERO, FE_DOWNWARD, FE_UPWARD, FE_TOWARDZERO};
+ return kRounding[rm];
+}
+
+} // namespace intrinsics
+
+} // namespace berberis
+
+#endif // BERBERIS_INTRINSICS_GUEST_RISCV64_FPSTATE_H_
diff --git a/kernel_api/fcntl_emulation.cc b/kernel_api/fcntl_emulation.cc
index e48097d4..6c74b49d 100644
--- a/kernel_api/fcntl_emulation.cc
+++ b/kernel_api/fcntl_emulation.cc
@@ -32,6 +32,7 @@
#include <cerrno>
+#include "berberis/base/checks.h"
#include "berberis/kernel_api/open_emulation.h"
#include "berberis/kernel_api/tracing.h"
@@ -62,9 +63,49 @@ static_assert(F_SETLEASE == 1024);
static_assert(F_GETLEASE == 1025);
static_assert(F_NOTIFY == 1026);
+#if !defined(ANDROID_HOST_MUSL)
static_assert(F_GETLK == 5);
static_assert(F_SETLK == 6);
static_assert(F_SETLKW == 7);
+#endif
+
+#define GUEST_F_GETLK 5
+#define GUEST_F_SETLK 6
+#define GUEST_F_SETLKW 7
+
+#if defined(ANDROID_HOST_MUSL)
+// Musl only has a 64-bit flock that it uses for flock and flock64.
+
+struct Guest_flock {
+ int16_t l_type;
+ int16_t l_whence;
+ int32_t l_start;
+ int32_t l_len;
+ int32_t l_pid;
+};
+
+const struct flock64* ConvertGuestFlockToHostFlock64(const Guest_flock* guest,
+ struct flock64* host) {
+ if (!guest) {
+ return nullptr;
+ }
+ *host = {guest->l_type, guest->l_whence, guest->l_start, guest->l_len, guest->l_pid};
+ return host;
+}
+
+void ConvertHostFlock64ToGuestFlock(const struct flock64* host, Guest_flock* guest) {
+ CHECK_NE(guest, nullptr);
+ CHECK_LE(host->l_start, INT32_MAX);
+ CHECK_GE(host->l_start, INT32_MIN);
+ CHECK_LE(host->l_len, INT32_MAX);
+ CHECK_GE(host->l_len, INT32_MIN);
+ *guest = {host->l_type,
+ host->l_whence,
+ static_cast<int32_t>(host->l_start),
+ static_cast<int32_t>(host->l_len),
+ host->l_pid};
+}
+#endif
namespace berberis {
@@ -102,10 +143,29 @@ int GuestFcntl(int fd, int cmd, long arg_3) {
#if defined(F_GET_SEALS)
case F_GET_SEALS:
#endif
- case F_SETLK:
- case F_SETLKW:
- case F_GETLK:
+ case GUEST_F_SETLK:
+ case GUEST_F_SETLKW:
+ case GUEST_F_GETLK:
+#if defined(ANDROID_HOST_MUSL)
+ {
+ // Musl only has a 64-bit flock for both flock and flock64, translate flock calls to flock64.
+ Guest_flock* guest_flock = reinterpret_cast<Guest_flock*>(arg_3);
+ struct flock64 host_flock64;
+ // In case of GETLK input flock describes region
+ // to check, thus conversion is also required.
+ auto result = fcntl(fd,
+ cmd + F_SETLK - GUEST_F_SETLK,
+ ConvertGuestFlockToHostFlock64(guest_flock, &host_flock64));
+ if (result == 0 && cmd == GUEST_F_GETLK) {
+ // Output contains the result of lock check.
+ ConvertHostFlock64ToGuestFlock(&host_flock64, guest_flock);
+ }
+ return result;
+ }
+#else
+ // struct flock compatibility is checked above.
return fcntl(fd, cmd, arg_3);
+#endif
case F_SETFL:
return fcntl(fd, cmd, ToHostOpenFlags(arg_3));
default: