/* * 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)); }