Cody Schuffelen | 134ff03 | 2019-11-22 00:25:32 -0800 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | #include "common/vsoc/lib/region_view.h" |
| 17 | |
| 18 | #define LOG_TAG "vsoc: region_host" |
| 19 | |
| 20 | #include <stdio.h> |
| 21 | #include <string.h> |
| 22 | #include <linux/futex.h> |
| 23 | #include <sys/mman.h> |
| 24 | #include <sys/socket.h> |
| 25 | #include <sys/syscall.h> |
| 26 | #include <sys/types.h> |
| 27 | #include <unistd.h> |
| 28 | |
| 29 | #include <iomanip> |
| 30 | #include <sstream> |
| 31 | #include <thread> |
| 32 | #include <vector> |
| 33 | |
| 34 | #include <glog/logging.h> |
| 35 | |
| 36 | #include "common/libs/fs/shared_fd.h" |
| 37 | #include "common/libs/fs/shared_select.h" |
| 38 | |
| 39 | using cvd::SharedFD; |
| 40 | |
| 41 | namespace { |
| 42 | |
| 43 | class HostRegionControl : public vsoc::RegionControl { |
| 44 | public: |
| 45 | HostRegionControl(const char* region_name, |
| 46 | const SharedFD& incoming_interrupt_fd, |
| 47 | const SharedFD& outgoing_interrupt_fd, |
| 48 | const SharedFD& shared_memory_fd) |
| 49 | : region_name_{region_name}, |
| 50 | incoming_interrupt_fd_{incoming_interrupt_fd}, |
| 51 | outgoing_interrupt_fd_{outgoing_interrupt_fd}, |
| 52 | shared_memory_fd_{shared_memory_fd} {} |
| 53 | |
| 54 | int CreateFdScopedPermission(const char* /*managed_region_name*/, |
| 55 | uint32_t /*owner_offset*/, |
| 56 | uint32_t /*owned_val*/, |
| 57 | uint32_t /*begin_offset*/, |
| 58 | uint32_t /*end_offset*/) override { |
| 59 | return -1; |
| 60 | } |
| 61 | |
| 62 | bool InitializeRegion(); |
| 63 | |
| 64 | virtual bool InterruptPeer() override { |
| 65 | uint64_t one = 1; |
| 66 | ssize_t rval = outgoing_interrupt_fd_->Write(&one, sizeof(one)); |
| 67 | if (rval != sizeof(one)) { |
| 68 | LOG(FATAL) << __FUNCTION__ << ": rval (" << rval << ") != sizeof(one))"; |
| 69 | return false; |
| 70 | } |
| 71 | return true; |
| 72 | } |
| 73 | |
| 74 | // Wake the local signal table scanner. Primarily used during shutdown |
| 75 | virtual void InterruptSelf() override { |
| 76 | uint64_t one = 1; |
| 77 | ssize_t rval = incoming_interrupt_fd_->Write(&one, sizeof(one)); |
| 78 | if (rval != sizeof(one)) { |
| 79 | LOG(FATAL) << __FUNCTION__ << ": rval (" << rval << ") != sizeof(one))"; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | virtual void WaitForInterrupt() override { |
| 84 | // Check then act isn't a problem here: the other side does |
| 85 | // the following things in exactly this order: |
| 86 | // 1. exchanges 1 with interrupt_signalled |
| 87 | // 2. if interrupt_signalled was 0 it increments the eventfd |
| 88 | // eventfd increments are persistent, so if interrupt_signalled was set |
| 89 | // back to 1 while we are going to sleep the sleep will return |
| 90 | // immediately. |
| 91 | uint64_t missed{}; |
| 92 | cvd::SharedFDSet readset; |
| 93 | readset.Set(incoming_interrupt_fd_); |
| 94 | cvd::Select(&readset, NULL, NULL, NULL); |
| 95 | ssize_t rval = incoming_interrupt_fd_->Read(&missed, sizeof(missed)); |
| 96 | if (rval != sizeof(missed)) { |
| 97 | LOG(FATAL) << __FUNCTION__ << ": rval (" << rval |
| 98 | << ") != sizeof(missed)), are there more than one threads " |
| 99 | "waiting for interrupts?"; |
| 100 | } |
| 101 | if (!missed) { |
| 102 | LOG(FATAL) << __FUNCTION__ << ": woke with 0 interrupts"; |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | virtual void* Map() override { |
| 107 | if (region_base_) { |
| 108 | return region_base_; |
| 109 | } |
| 110 | // Now actually map the region |
| 111 | region_base_ = |
| 112 | shared_memory_fd_->Mmap(0, region_size(), PROT_READ | PROT_WRITE, |
| 113 | MAP_SHARED, region_desc_.region_begin_offset); |
| 114 | if (region_base_ == MAP_FAILED) { |
| 115 | LOG(FATAL) << "mmap failed for offset " |
| 116 | << region_desc_.region_begin_offset << " (" |
| 117 | << shared_memory_fd_->StrError() << ")"; |
| 118 | region_base_ = nullptr; |
| 119 | } |
| 120 | return region_base_; |
| 121 | } |
| 122 | |
| 123 | |
| 124 | virtual int SignalSelf(uint32_t offset) override { |
| 125 | return syscall(SYS_futex, region_offset_to_pointer<int32_t*>(offset), |
| 126 | FUTEX_WAKE, -1, nullptr, nullptr, 0); |
| 127 | } |
| 128 | |
| 129 | virtual int WaitForSignal(uint32_t offset, uint32_t expected_value) override { |
| 130 | return syscall(SYS_futex, region_offset_to_pointer<int32_t*>(offset), |
| 131 | FUTEX_WAIT, expected_value, nullptr, nullptr, 0); |
| 132 | } |
| 133 | |
| 134 | protected: |
| 135 | const char* region_name_{}; |
| 136 | cvd::SharedFD incoming_interrupt_fd_; |
| 137 | cvd::SharedFD outgoing_interrupt_fd_; |
| 138 | cvd::SharedFD shared_memory_fd_; |
| 139 | }; |
| 140 | |
| 141 | // Default path to the ivshmem_server socket. This can vary when we're |
| 142 | // launching multiple CVDs. |
| 143 | constexpr int kMaxSupportedProtocolVersion = 0; |
| 144 | |
| 145 | bool HostRegionControl::InitializeRegion() { |
| 146 | size_t region_name_len = strlen(region_name_); |
| 147 | if (region_name_len >= VSOC_DEVICE_NAME_SZ) { |
| 148 | LOG(FATAL) << "Region name length (" << region_name_len << ") not < " |
| 149 | << VSOC_DEVICE_NAME_SZ; |
| 150 | return false; |
| 151 | } |
| 152 | vsoc_shm_layout_descriptor layout; |
| 153 | ssize_t rval = shared_memory_fd_->Pread(&layout, sizeof(layout), 0); |
| 154 | if (rval != sizeof(layout)) { |
| 155 | LOG(FATAL) << "Unable to read layout, rval=" << rval << " (" |
| 156 | << shared_memory_fd_->StrError() << ")"; |
| 157 | return false; |
| 158 | } |
| 159 | if (layout.major_version != CURRENT_VSOC_LAYOUT_MAJOR_VERSION) { |
| 160 | LOG(FATAL) << "Incompatible major version: saw " << layout.major_version |
| 161 | << " wanted " << CURRENT_VSOC_LAYOUT_MAJOR_VERSION; |
| 162 | } |
| 163 | std::vector<vsoc_device_region> descriptors; |
| 164 | descriptors.resize(layout.region_count); |
| 165 | ssize_t wanted = sizeof(vsoc_device_region) * layout.region_count; |
| 166 | rval = shared_memory_fd_->Pread(descriptors.data(), wanted, |
| 167 | layout.vsoc_region_desc_offset); |
| 168 | if (rval != wanted) { |
| 169 | LOG(FATAL) << "Unable to read region descriptors, rval=" << rval << " (" |
| 170 | << shared_memory_fd_->StrError() << ")"; |
| 171 | return false; |
| 172 | } |
| 173 | for (const auto& desc : descriptors) { |
| 174 | if (!strcmp(region_name_, desc.device_name)) { |
| 175 | region_desc_ = desc; |
| 176 | return true; |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | std::ostringstream buf; |
| 181 | for (const auto& desc : descriptors) { |
| 182 | buf << " " << desc.device_name; |
| 183 | } |
| 184 | LOG(FATAL) << "Region name of " << region_name_ |
| 185 | << " not found among:" << buf.str(); |
| 186 | return false; |
| 187 | } |
| 188 | } // namespace |
| 189 | |
| 190 | std::shared_ptr<vsoc::RegionControl> vsoc::RegionControl::Open( |
| 191 | const char* region_name, const char* domain) { |
| 192 | AutoFreeBuffer msg; |
| 193 | |
| 194 | SharedFD region_server = |
| 195 | SharedFD::SocketLocalClient(domain, false, SOCK_STREAM); |
| 196 | if (!region_server->IsOpen()) { |
| 197 | LOG(FATAL) << "Could not contact ivshmem_server (" |
| 198 | << region_server->StrError() << ")"; |
| 199 | return nullptr; |
| 200 | } |
| 201 | |
| 202 | // Check server protocol version. |
| 203 | uint32_t protocol_version; |
| 204 | ssize_t bytes = region_server->Recv(&protocol_version, |
| 205 | sizeof(protocol_version), MSG_NOSIGNAL); |
| 206 | if (bytes != sizeof(protocol_version)) { |
| 207 | LOG(FATAL) << "Failed to recv protocol version; res=" << bytes << " (" |
| 208 | << region_server->StrError() << ")"; |
| 209 | return nullptr; |
| 210 | } |
| 211 | |
| 212 | if (protocol_version > kMaxSupportedProtocolVersion) { |
| 213 | LOG(FATAL) << "Unsupported protocol version " << protocol_version |
| 214 | << "; max supported version is " << kMaxSupportedProtocolVersion; |
| 215 | return nullptr; |
| 216 | } |
| 217 | |
| 218 | // Send requested region. |
| 219 | int16_t size = strlen(region_name); |
| 220 | bytes = region_server->Send(&size, sizeof(size), MSG_NOSIGNAL); |
| 221 | if (bytes != sizeof(size)) { |
| 222 | LOG(FATAL) << "Failed to send region name length; res=" << bytes << " (" |
| 223 | << region_server->StrError() << ")"; |
| 224 | return nullptr; |
| 225 | } |
| 226 | |
| 227 | bytes = region_server->Send(region_name, size, MSG_NOSIGNAL); |
| 228 | if (bytes != size) { |
| 229 | LOG(FATAL) << "Failed to send region name; res=" << bytes << " (" |
| 230 | << region_server->StrError() << ")"; |
| 231 | return nullptr; |
| 232 | } |
| 233 | |
| 234 | // Receive control sockets. |
| 235 | uint64_t control_data; |
| 236 | struct iovec iov { |
| 237 | &control_data, sizeof(control_data) |
| 238 | }; |
| 239 | cvd::InbandMessageHeader hdr{}; |
| 240 | hdr.msg_iov = &iov; |
| 241 | hdr.msg_iovlen = 1; |
| 242 | SharedFD fds[3]; |
| 243 | bytes = region_server->RecvMsgAndFDs(hdr, 0, &fds); |
| 244 | if (bytes != sizeof(control_data)) { |
| 245 | LOG(FATAL) << "Failed to complete handshake; res=" << bytes << " (" |
| 246 | << region_server->StrError() << ")"; |
| 247 | return nullptr; |
| 248 | } |
| 249 | HostRegionControl* rval = |
| 250 | new HostRegionControl(region_name, fds[0], fds[1], fds[2]); |
| 251 | if (!rval) { |
| 252 | return nullptr; |
| 253 | } |
| 254 | // Search for the region header |
| 255 | if (!rval->InitializeRegion()) { |
| 256 | // We already logged, so we can just bail out. |
| 257 | return nullptr; |
| 258 | } |
| 259 | return std::shared_ptr<RegionControl>(rval); |
| 260 | } |