Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [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 | */ |
Greg Hartman | 86b114a | 2017-11-03 21:04:14 -0700 | [diff] [blame] | 16 | #include "host/vsoc/lib/region_control.h" |
Greg Hartman | efe487a | 2017-11-10 20:39:06 -0800 | [diff] [blame^] | 17 | #include "common/vsoc/lib/region_view.h" |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 18 | |
| 19 | #define LOG_TAG "vsoc: region_host" |
| 20 | |
| 21 | #include <stdio.h> |
| 22 | #include <string.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 | |
Greg Hartman | efe487a | 2017-11-10 20:39:06 -0800 | [diff] [blame^] | 29 | #include <iomanip> |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 30 | #include <sstream> |
| 31 | #include <thread> |
| 32 | #include <vector> |
| 33 | |
Greg Hartman | efe487a | 2017-11-10 20:39:06 -0800 | [diff] [blame^] | 34 | #include <gflags/gflags.h> |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 35 | #include <glog/logging.h> |
| 36 | |
| 37 | #include "common/libs/fs/shared_fd.h" |
| 38 | #include "common/libs/fs/shared_select.h" |
| 39 | |
| 40 | using avd::SharedFD; |
| 41 | |
Greg Hartman | efe487a | 2017-11-10 20:39:06 -0800 | [diff] [blame^] | 42 | DEFINE_int32(instance, 1, "Instance number. Must be unique."); |
| 43 | DEFINE_string(vsoc_run_dir, "/var/run/cvd-", |
| 44 | "Path to the directory holding vsoc resources"); |
| 45 | DEFINE_string(vsoc_shm_client_sock, "ivshmem_socket_client", |
| 46 | "Name of the VSoC client socket"); |
| 47 | |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 48 | namespace { |
Greg Hartman | efe487a | 2017-11-10 20:39:06 -0800 | [diff] [blame^] | 49 | |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 50 | class HostRegionControl : public vsoc::RegionControl { |
| 51 | public: |
| 52 | HostRegionControl(const char* region_name, |
| 53 | const SharedFD& incoming_interrupt_fd, |
| 54 | const SharedFD& outgoing_interrupt_fd, |
| 55 | const SharedFD& shared_memory_fd) |
| 56 | : region_name_{region_name}, |
| 57 | incoming_interrupt_fd_{incoming_interrupt_fd}, |
| 58 | outgoing_interrupt_fd_{outgoing_interrupt_fd}, |
| 59 | shared_memory_fd_{shared_memory_fd} {} |
| 60 | |
| 61 | int CreateFdScopedPermission(const char* managed_region_name, |
| 62 | vsoc_reg_off_t owner_offset, uint32_t owned_val, |
| 63 | vsoc_reg_off_t begin_offset, |
| 64 | vsoc_reg_off_t end_offset) override { |
| 65 | return -1; |
| 66 | } |
| 67 | |
| 68 | bool InitializeRegion(); |
| 69 | |
| 70 | virtual bool InterruptPeer() override { |
| 71 | uint64_t one = 1; |
| 72 | ssize_t rval = outgoing_interrupt_fd_->Write(&one, sizeof(one)); |
| 73 | if (rval != sizeof(one)) { |
| 74 | LOG(FATAL) << __FUNCTION__ << ": rval (" << rval << ") != sizeof(one))"; |
| 75 | return false; |
| 76 | } |
| 77 | return true; |
| 78 | } |
| 79 | |
| 80 | // Wake the local signal table scanner. Primarily used during shutdown |
| 81 | virtual void InterruptSelf() override { |
| 82 | uint64_t one = 1; |
| 83 | ssize_t rval = incoming_interrupt_fd_->Write(&one, sizeof(one)); |
| 84 | if (rval != sizeof(one)) { |
| 85 | LOG(FATAL) << __FUNCTION__ << ": rval (" << rval << ") != sizeof(one))"; |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | virtual void WaitForInterrupt() override { |
| 90 | // Check then act isn't a problem here: the other side does |
| 91 | // the following things in exactly this order: |
| 92 | // 1. exchanges 1 with interrupt_signalled |
| 93 | // 2. if interrupt_signalled was 0 it increments the eventfd |
| 94 | // eventfd increments are persistent, so if interrupt_signalled was set |
| 95 | // back to 1 while we are going to sleep the sleep will return |
| 96 | // immediately. |
| 97 | uint64_t missed{}; |
| 98 | avd::SharedFDSet readset; |
| 99 | readset.Set(incoming_interrupt_fd_); |
| 100 | avd::Select(&readset, NULL, NULL, NULL); |
| 101 | ssize_t rval = incoming_interrupt_fd_->Read(&missed, sizeof(missed)); |
| 102 | if (rval != sizeof(missed)) { |
| 103 | LOG(FATAL) << __FUNCTION__ << ": rval (" << rval |
| 104 | << ") != sizeof(missed))"; |
| 105 | } |
| 106 | if (!missed) { |
| 107 | LOG(FATAL) << __FUNCTION__ << ": woke with 0 interrupts"; |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | virtual void* Map() override { |
| 112 | if (region_base_) { |
| 113 | return region_base_; |
| 114 | } |
| 115 | // Now actually map the region |
| 116 | region_base_ = |
| 117 | shared_memory_fd_->Mmap(0, region_size(), PROT_READ | PROT_WRITE, |
| 118 | MAP_SHARED, region_desc_.region_begin_offset); |
| 119 | if (region_base_ == MAP_FAILED) { |
| 120 | LOG(FATAL) << "mmap failed for offset " |
| 121 | << region_desc_.region_begin_offset << " (" |
| 122 | << shared_memory_fd_->StrError() << ")"; |
| 123 | region_base_ = nullptr; |
| 124 | } |
| 125 | return region_base_; |
| 126 | } |
| 127 | |
| 128 | protected: |
| 129 | const char* region_name_{}; |
| 130 | avd::SharedFD incoming_interrupt_fd_; |
| 131 | avd::SharedFD outgoing_interrupt_fd_; |
| 132 | avd::SharedFD shared_memory_fd_; |
| 133 | }; |
| 134 | |
| 135 | // Default path to the ivshmem_server socket. This can vary when we're |
| 136 | // launching multiple AVDs. |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 137 | constexpr int kMaxSupportedProtocolVersion = 0; |
| 138 | |
| 139 | bool HostRegionControl::InitializeRegion() { |
| 140 | size_t region_name_len = strlen(region_name_); |
| 141 | if (region_name_len >= sizeof(vsoc_device_name)) { |
| 142 | LOG(FATAL) << "Region name length (" << region_name_len << ") not < " |
| 143 | << sizeof(vsoc_device_name); |
| 144 | return false; |
| 145 | } |
| 146 | vsoc_shm_layout_descriptor layout; |
| 147 | ssize_t rval = shared_memory_fd_->Pread(&layout, sizeof(layout), 0); |
| 148 | if (rval != sizeof(layout)) { |
| 149 | LOG(FATAL) << "Unable to read layout, rval=" << rval << " (" |
| 150 | << shared_memory_fd_->StrError() << ")"; |
| 151 | return false; |
| 152 | } |
| 153 | if (layout.major_version != CURRENT_VSOC_LAYOUT_MAJOR_VERSION) { |
| 154 | LOG(FATAL) << "Incompatible major version: saw " << layout.major_version |
| 155 | << " wanted " << CURRENT_VSOC_LAYOUT_MAJOR_VERSION; |
| 156 | } |
| 157 | std::vector<vsoc_device_region> descriptors; |
| 158 | descriptors.resize(layout.region_count); |
| 159 | ssize_t wanted = sizeof(vsoc_device_region) * layout.region_count; |
| 160 | rval = shared_memory_fd_->Pread(descriptors.data(), wanted, |
| 161 | layout.vsoc_region_desc_offset); |
| 162 | if (rval != wanted) { |
| 163 | LOG(FATAL) << "Unable to read region descriptors, rval=" << rval << " (" |
| 164 | << shared_memory_fd_->StrError() << ")"; |
| 165 | return false; |
| 166 | } |
| 167 | for (const auto& desc : descriptors) { |
| 168 | if (!strcmp(region_name_, desc.device_name)) { |
| 169 | region_desc_ = desc; |
| 170 | return true; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | std::ostringstream buf; |
| 175 | for (const auto& desc : descriptors) { |
| 176 | buf << " " << desc.device_name; |
| 177 | } |
| 178 | LOG(FATAL) << "Region name of " << region_name_ |
| 179 | << " not found among:" << buf.str(); |
| 180 | return false; |
| 181 | } |
| 182 | } // namespace |
| 183 | |
Greg Hartman | efe487a | 2017-11-10 20:39:06 -0800 | [diff] [blame^] | 184 | std::string vsoc::GetShmClientSocketPath() { |
| 185 | std::ostringstream stream; |
| 186 | stream << FLAGS_vsoc_run_dir << std::setfill('0') << std::setw(2) |
| 187 | << FLAGS_instance << "/" << FLAGS_vsoc_shm_client_sock; |
| 188 | return stream.str(); |
| 189 | } |
| 190 | |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 191 | std::shared_ptr<vsoc::RegionControl> vsoc::RegionControl::Open( |
| 192 | const char* region_name, const char* domain) { |
| 193 | AutoFreeBuffer msg; |
Greg Hartman | efe487a | 2017-11-10 20:39:06 -0800 | [diff] [blame^] | 194 | std::string owned_domain; |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 195 | |
| 196 | if (!domain) { |
Greg Hartman | efe487a | 2017-11-10 20:39:06 -0800 | [diff] [blame^] | 197 | owned_domain = GetShmClientSocketPath(); |
| 198 | domain = owned_domain.c_str(); |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 199 | } |
| 200 | SharedFD region_server = |
| 201 | SharedFD::SocketLocalClient(domain, false, SOCK_STREAM); |
| 202 | if (!region_server->IsOpen()) { |
| 203 | LOG(FATAL) << "Could not contact ivshmem_server (" |
| 204 | << region_server->StrError() << ")"; |
| 205 | return nullptr; |
| 206 | } |
| 207 | |
| 208 | // Check server protocol version. |
| 209 | uint32_t protocol_version; |
| 210 | ssize_t bytes = region_server->Recv(&protocol_version, |
| 211 | sizeof(protocol_version), MSG_NOSIGNAL); |
| 212 | if (bytes != sizeof(protocol_version)) { |
| 213 | LOG(FATAL) << "Failed to recv protocol version; res=" << bytes << " (" |
| 214 | << region_server->StrError() << ")"; |
| 215 | return nullptr; |
| 216 | } |
| 217 | |
| 218 | if (protocol_version > kMaxSupportedProtocolVersion) { |
| 219 | LOG(FATAL) << "Unsupported protocol version " << protocol_version |
| 220 | << "; max supported version is " << kMaxSupportedProtocolVersion; |
| 221 | return nullptr; |
| 222 | } |
| 223 | |
| 224 | // Send requested region. |
| 225 | int16_t size = strlen(region_name); |
| 226 | bytes = region_server->Send(&size, sizeof(size), MSG_NOSIGNAL); |
| 227 | if (bytes != sizeof(size)) { |
| 228 | LOG(FATAL) << "Failed to send region name length; res=" << bytes << " (" |
| 229 | << region_server->StrError() << ")"; |
| 230 | return nullptr; |
| 231 | } |
| 232 | |
| 233 | bytes = region_server->Send(region_name, size, MSG_NOSIGNAL); |
| 234 | if (bytes != size) { |
| 235 | LOG(FATAL) << "Failed to send region name; res=" << bytes << " (" |
| 236 | << region_server->StrError() << ")"; |
| 237 | return nullptr; |
| 238 | } |
| 239 | |
| 240 | // Receive control sockets. |
| 241 | uint64_t control_data; |
| 242 | struct iovec iov { |
| 243 | &control_data, sizeof(control_data) |
| 244 | }; |
| 245 | avd::InbandMessageHeader hdr{}; |
| 246 | hdr.msg_iov = &iov; |
| 247 | hdr.msg_iovlen = 1; |
| 248 | SharedFD fds[3]; |
| 249 | bytes = region_server->RecvMsgAndFDs(hdr, 0, &fds); |
| 250 | if (bytes != sizeof(control_data)) { |
| 251 | LOG(FATAL) << "Failed to complete handshake; res=" << bytes << " (" |
| 252 | << region_server->StrError() << ")"; |
| 253 | return nullptr; |
| 254 | } |
| 255 | HostRegionControl* rval = |
| 256 | new HostRegionControl(region_name, fds[0], fds[1], fds[2]); |
| 257 | if (!rval) { |
| 258 | return nullptr; |
| 259 | } |
| 260 | // Search for the region header |
| 261 | if (!rval->InitializeRegion()) { |
| 262 | // We already logged, so we can just bail out. |
| 263 | return nullptr; |
| 264 | } |
| 265 | return std::shared_ptr<RegionControl>(rval); |
| 266 | } |