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:
HostRegionControl(const char * region_name,const SharedFD & incoming_interrupt_fd,const SharedFD & outgoing_interrupt_fd,const SharedFD & shared_memory_fd)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
CreateFdScopedPermission(const char *,uint32_t,uint32_t,uint32_t,uint32_t)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
InterruptPeer()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
InterruptSelf()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
WaitForInterrupt()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
Map()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
SignalSelf(uint32_t offset)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
WaitForSignal(uint32_t offset,uint32_t expected_value)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
InitializeRegion()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
Open(const char * region_name,const char * domain)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 }
261