• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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