diff options
-rw-r--r-- | base/Android.bp | 11 | ||||
-rw-r--r-- | base/exec_region_elf_backed.cc | 67 | ||||
-rw-r--r-- | base/exec_region_elf_backed_test.cc | 71 | ||||
-rw-r--r-- | decoder/include/berberis/decoder/riscv64/decoder.h | 64 | ||||
-rw-r--r-- | decoder/include/berberis/decoder/riscv64/semantics_player.h | 13 | ||||
-rw-r--r-- | exec_region/Android.bp | 27 | ||||
-rw-r--r-- | exec_region/exec_region.cc | 15 | ||||
-rw-r--r-- | exec_region/sections.ld | 55 | ||||
-rw-r--r-- | guest_state/include/berberis/guest_state/guest_state_riscv64.h | 15 | ||||
-rw-r--r-- | interpreter/Android.bp | 2 | ||||
-rw-r--r-- | interpreter/riscv64/interpreter.cc | 43 | ||||
-rw-r--r-- | interpreter/riscv64/interpreter_test.cc | 20 | ||||
-rw-r--r-- | intrinsics/Android.bp | 7 | ||||
-rw-r--r-- | intrinsics/include/berberis/intrinsics/guest_fpstate.h | 14 | ||||
-rw-r--r-- | intrinsics/include/berberis/intrinsics/riscv64/guest_fpstate.h | 58 | ||||
-rw-r--r-- | kernel_api/fcntl_emulation.cc | 66 |
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: |