| /* |
| * Copyright (C) 2017 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 "common/vsoc/lib/region_view.h" |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include <string> |
| #include <thread> |
| |
| #include <android-base/logging.h> |
| #include <uapi/vsoc_shm.h> |
| |
| using cvd::SharedFD; |
| |
| namespace { |
| class GuestRegionControl : public vsoc::RegionControl { |
| public: |
| explicit GuestRegionControl(const SharedFD& region_fd, |
| const vsoc_device_region& desc) |
| : region_fd_{region_fd} { |
| region_desc_ = desc; |
| } |
| virtual bool InterruptPeer() override; |
| virtual void InterruptSelf() override; |
| virtual void WaitForInterrupt() override; |
| virtual void* Map() override; |
| virtual int SignalSelf(uint32_t offset) override; |
| virtual int WaitForSignal(uint32_t offset, uint32_t expected_value) override; |
| |
| protected: |
| int CreateFdScopedPermission(const char* managed_region_name, |
| uint32_t owner_offset, uint32_t owned_val, |
| uint32_t begin_offset, |
| uint32_t end_offset) override; |
| cvd::SharedFD region_fd_; |
| }; |
| |
| std::string device_path_from_name(const char* region_name) { |
| return std::string("/dev/") + region_name; |
| } |
| |
| bool GuestRegionControl::InterruptPeer() { |
| int rval = region_fd_->Ioctl(VSOC_SEND_INTERRUPT_TO_HOST, 0); |
| if ((rval == -1) && (errno != EBUSY)) { |
| LOG(INFO) << __FUNCTION__ << ": ioctl failed (" << strerror(errno) << ")"; |
| } |
| return !rval; |
| } |
| |
| void GuestRegionControl::InterruptSelf() { |
| region_fd_->Ioctl(VSOC_SELF_INTERRUPT, 0); |
| } |
| |
| void GuestRegionControl::WaitForInterrupt() { |
| region_fd_->Ioctl(VSOC_WAIT_FOR_INCOMING_INTERRUPT, 0); |
| } |
| |
| int GuestRegionControl::SignalSelf(uint32_t offset) { |
| return region_fd_->Ioctl(VSOC_COND_WAKE, reinterpret_cast<void*>(offset)); |
| } |
| |
| int GuestRegionControl::WaitForSignal(uint32_t offset, |
| uint32_t expected_value) { |
| struct vsoc_cond_wait wait; |
| wait.offset = offset; |
| wait.value = expected_value; |
| wait.wake_time_sec = 0; |
| wait.wake_time_nsec = 0; |
| wait.wait_type = VSOC_WAIT_IF_EQUAL; |
| wait.wakes = 1000; |
| wait.reserved_1 = 0; |
| int rval = region_fd_->Ioctl(VSOC_COND_WAIT, &wait); |
| if (rval == -1) { |
| return rval; |
| } |
| // Clamp the number of wakes if it overflows an integer. |
| rval = wait.wakes; |
| if (rval >= 0) { |
| return rval; |
| } |
| return INT_MAX; |
| } |
| |
| int GuestRegionControl::CreateFdScopedPermission( |
| const char* managed_region_name, uint32_t owner_offset, |
| uint32_t owned_value, uint32_t begin_offset, |
| uint32_t end_offset) { |
| if (!region_fd_->IsOpen()) { |
| LOG(FATAL) << "Can't create permission before opening controller region"; |
| return -EINVAL; |
| } |
| int managed_region_fd = |
| open(device_path_from_name(managed_region_name).c_str(), O_RDWR); |
| if (managed_region_fd < 0) { |
| int errno_ = errno; |
| LOG(FATAL) << "Can't open managed region: " << managed_region_name; |
| return -errno_; |
| } |
| |
| fd_scoped_permission_arg perm; |
| perm.perm.begin_offset = begin_offset; |
| perm.perm.end_offset = end_offset; |
| perm.perm.owned_value = owned_value; |
| perm.perm.owner_offset = owner_offset; |
| perm.managed_region_fd = managed_region_fd; |
| LOG(INFO) << "owner offset: " << perm.perm.owner_offset; |
| int retval = region_fd_->Ioctl(VSOC_CREATE_FD_SCOPED_PERMISSION, &perm); |
| if (retval) { |
| int errno_ = errno; |
| close(managed_region_fd); |
| if (errno_ != EBUSY) { |
| LOG(FATAL) << "Unable to create fd scoped permission (" |
| << strerror(errno_) << ")"; |
| } |
| return -errno_; |
| } |
| return managed_region_fd; |
| } |
| |
| void* GuestRegionControl::Map() { |
| region_base_ = |
| region_fd_->Mmap(0, region_size(), PROT_READ | PROT_WRITE, MAP_SHARED, 0); |
| if (region_base_ == MAP_FAILED) { |
| LOG(FATAL) << "mmap failed (" << region_fd_->StrError() << ")"; |
| region_base_ = nullptr; |
| } |
| return region_base_; |
| } |
| } // namespace |
| |
| // domain is here to ensure that this method has the same signature as the |
| // method on host regions. |
| std::shared_ptr<vsoc::RegionControl> vsoc::RegionControl::Open( |
| const char* region_name) { |
| std::string path = device_path_from_name(region_name); |
| SharedFD fd = SharedFD::Open(path.c_str(), O_RDWR); |
| if (!fd->IsOpen()) { |
| LOG(FATAL) << "Unable to open region " << region_name << " (" |
| << fd->StrError() << ")"; |
| return nullptr; |
| } |
| vsoc_device_region desc; |
| if (fd->Ioctl(VSOC_DESCRIBE_REGION, &desc)) { |
| LOG(FATAL) << "Unable to obtain region descriptor (" << fd->StrError() |
| << ")"; |
| return nullptr; |
| } |
| return std::shared_ptr<vsoc::RegionControl>(new GuestRegionControl(fd, desc)); |
| } |