Home | History | Annotate | Download | only in launch
      1 /*
      2  * Copyright (C) 2018 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 
     17 #include "host/commands/launch/vsoc_shared_memory.h"
     18 
     19 #include <unistd.h>
     20 
     21 #include <map>
     22 #include <vector>
     23 
     24 #include "common/libs/fs/shared_fd.h"
     25 #include "common/libs/utils/size_utils.h"
     26 #include "common/vsoc/lib/vsoc_memory.h"
     27 #include "glog/logging.h"
     28 
     29 #include "uapi/vsoc_shm.h"
     30 
     31 namespace vsoc {
     32 
     33 namespace {
     34 
     35 uint32_t OffsetOfRegionData(const VSoCRegionLayout& layout) {
     36   uint32_t offset = 0;
     37   // Signal tables
     38   offset +=
     39       (1 << layout.guest_to_host_signal_table_log_size()) * sizeof(uint32_t);
     40   offset +=
     41       (1 << layout.host_to_guest_signal_table_log_size()) * sizeof(uint32_t);
     42   // Interrup signals
     43   offset += 2 * sizeof(uint32_t);
     44   return offset;
     45 }
     46 
     47 struct VSoCRegionAllocator {
     48   const VSoCRegionLayout* region_layout;
     49   uint32_t begin_offset;
     50   uint32_t region_size;
     51 
     52   VSoCRegionAllocator(const VSoCRegionLayout& layout, uint32_t offset,
     53                       uint32_t requested_layout_increase = 0)
     54       : region_layout(&layout),
     55         begin_offset(offset),
     56         region_size(cvd::AlignToPageSize(OffsetOfRegionData(layout) +
     57                                          layout.layout_size() +
     58                                          requested_layout_increase)) {}
     59 };
     60 
     61 // Writes a region's signal table layout to shared memory. Returns the region
     62 // offset of free memory after the table and interrupt signaled word.
     63 uint32_t WriteSignalTableDescription(vsoc_signal_table_layout* layout,
     64                                      uint32_t offset, int log_size) {
     65   layout->num_nodes_lg2 = log_size;
     66   // First the signal table
     67   layout->futex_uaddr_table_offset = offset;
     68   offset += (1 << log_size) * sizeof(uint32_t);
     69   // Then the interrupt signaled word
     70   layout->interrupt_signalled_offset = offset;
     71   offset += sizeof(uint32_t);
     72   return offset;
     73 }
     74 
     75 // Writes a region's layout description to shared memory
     76 void WriteRegionDescription(vsoc_device_region* shmem_region_desc,
     77                             const VSoCRegionAllocator& allocator) {
     78   // Region versions are deprecated, write some sensible value
     79   shmem_region_desc->current_version = 0;
     80   shmem_region_desc->min_compatible_version = 0;
     81 
     82   shmem_region_desc->region_begin_offset = allocator.begin_offset;
     83   shmem_region_desc->region_end_offset =
     84       allocator.begin_offset + allocator.region_size;
     85   shmem_region_desc->offset_of_region_data =
     86       OffsetOfRegionData(*allocator.region_layout);
     87   strncpy(shmem_region_desc->device_name,
     88           allocator.region_layout->region_name(), VSOC_DEVICE_NAME_SZ - 1);
     89   shmem_region_desc->device_name[VSOC_DEVICE_NAME_SZ - 1] = '\0';
     90   // Guest to host signal table at the beginning of the region
     91   uint32_t offset = 0;
     92   offset = WriteSignalTableDescription(
     93       &shmem_region_desc->guest_to_host_signal_table, offset,
     94       allocator.region_layout->guest_to_host_signal_table_log_size());
     95   // Host to guest signal table right after
     96   offset = WriteSignalTableDescription(
     97       &shmem_region_desc->host_to_guest_signal_table, offset,
     98       allocator.region_layout->host_to_guest_signal_table_log_size());
     99   // Double check that the region metadata does not collide with the data
    100   if (offset > shmem_region_desc->offset_of_region_data) {
    101     LOG(FATAL) << "Error: Offset of region data too small (is "
    102                << shmem_region_desc->offset_of_region_data << " should be "
    103                << offset << " ) for region "
    104                << allocator.region_layout->region_name() << ". This is a bug";
    105   }
    106 }
    107 
    108 void WriteLayout(void* shared_memory,
    109                  const std::vector<VSoCRegionAllocator>& allocators,
    110                  uint32_t file_size) {
    111   // Device header
    112   static_assert(CURRENT_VSOC_LAYOUT_MAJOR_VERSION == 2,
    113                 "Region layout code must be updated");
    114   auto header = reinterpret_cast<vsoc_shm_layout_descriptor*>(shared_memory);
    115   header->major_version = CURRENT_VSOC_LAYOUT_MAJOR_VERSION;
    116   header->minor_version = CURRENT_VSOC_LAYOUT_MINOR_VERSION;
    117   header->size = file_size;
    118   header->region_count = allocators.size();
    119 
    120   std::map<std::string, size_t> region_idx_by_name;
    121   for (size_t idx = 0; idx < allocators.size(); ++idx) {
    122     region_idx_by_name[allocators[idx].region_layout->region_name()] = idx;
    123   }
    124 
    125   // Region descriptions go right after the layout descriptor
    126   header->vsoc_region_desc_offset = sizeof(vsoc_shm_layout_descriptor);
    127   auto region_descriptions = reinterpret_cast<vsoc_device_region*>(header + 1);
    128   for (size_t idx = 0; idx < allocators.size(); ++idx) {
    129     auto shmem_region_desc = &region_descriptions[idx];
    130     const auto& region = *allocators[idx].region_layout;
    131     WriteRegionDescription(shmem_region_desc, allocators[idx]);
    132     // Handle managed_by links
    133     if (region.managed_by()) {
    134       auto manager_idx = region_idx_by_name.at(region.managed_by());
    135       if (manager_idx == VSOC_REGION_WHOLE) {
    136         LOG(FATAL) << "Region '" << region.region_name() << "' has owner "
    137                    << region.managed_by() << " with index " << manager_idx
    138                    << " which is the default value for regions without an "
    139                       "owner. Choose a different region to be at index "
    140                    << manager_idx
    141                    << ", make sure the chosen region is NOT the owner of any "
    142                       "other region";
    143       }
    144       shmem_region_desc->managed_by = manager_idx;
    145     } else {
    146       shmem_region_desc->managed_by = VSOC_REGION_WHOLE;
    147     }
    148   }
    149 }
    150 }  // namespace
    151 
    152 void CreateSharedMemoryFile(
    153     const std::string& path,
    154     const std::map<std::string, uint32_t>& layout_increases) {
    155   // TODO(ender): Lock the file after creation and check lock status upon second
    156   // execution attempt instead of throwing an error.
    157   LOG_IF(WARNING, unlink(path.c_str()) == 0)
    158       << "Removed existing instance of " << path
    159       << ". We currently don't know if another instance of daemon is running";
    160   auto shared_mem_fd = cvd::SharedFD::Open(
    161       path.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
    162   LOG_IF(FATAL, !shared_mem_fd->IsOpen())
    163       << "Error in creating shared_memory file: " << shared_mem_fd->StrError();
    164 
    165   auto region_layouts = VSoCMemoryLayout::Get()->GetRegions();
    166   std::vector<VSoCRegionAllocator> allocators;
    167   uint32_t file_size =
    168       cvd::AlignToPageSize(sizeof(vsoc_shm_layout_descriptor) +
    169                            region_layouts.size() * sizeof(vsoc_device_region));
    170   for (auto layout : region_layouts) {
    171     allocators.emplace_back(*layout, file_size /* offset */,
    172                             layout_increases.count(layout->region_name())
    173                                 ? layout_increases.at(layout->region_name())
    174                                 : 0);
    175     file_size += allocators.back().region_size;
    176   }
    177   file_size = cvd::RoundUpToNextPowerOf2(file_size);
    178 
    179   int truncate_res = shared_mem_fd->Truncate(file_size);
    180   LOG_IF(FATAL, truncate_res == -1)
    181       << "Error in sizing up the shared memory file: "
    182       << shared_mem_fd->StrError();
    183 
    184   void* mmap_addr =
    185       shared_mem_fd->Mmap(0, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, 0);
    186   LOG_IF(FATAL, mmap_addr == MAP_FAILED)
    187       << "Error mmaping file: " << strerror(errno);
    188   WriteLayout(mmap_addr, allocators, file_size);
    189   munmap(mmap_addr, file_size);
    190 }
    191 
    192 }  // namespace vsoc
    193