Moves dealing with VSoC layout to vsoc_lib library

After moving the logic to calculate region sizes to vsoc_lib the
knowledge about shared memory layout was split between two independent
modules. It makes sense to let the vsoc_lib library be in charge of
the details of the memory layout while keeping the ivsering just
knowing how to 'serve'.

The information about the memory layout that the ivserver and other
host processes need is accessible through a simple interface defined
in common/vsoc/lib/vsoc_memory.h.

Bug: 79602833
Test: run locally
Change-Id: Ib80bb42bd0d3cdb1dea635b2153258d31d7c92a7
diff --git a/common/vsoc/lib/vsoc_memory.cpp b/common/vsoc/lib/vsoc_memory.cpp
index f82b072..04f90bd 100644
--- a/common/vsoc/lib/vsoc_memory.cpp
+++ b/common/vsoc/lib/vsoc_memory.cpp
@@ -16,9 +16,14 @@
 
 #include "common/vsoc/lib/vsoc_memory.h"
 
+#include <string.h>
+#include <unistd.h>
+
+#include <map>
 #include <string>
 #include <type_traits>
 
+#include "common/libs/glog/logging.h"
 #include "common/vsoc/shm/audio_data_layout.h"
 #include "common/vsoc/shm/base.h"
 #include "common/vsoc/shm/e2e_test_region_layout.h"
@@ -29,52 +34,343 @@
 #include "common/vsoc/shm/socket_forward_layout.h"
 #include "common/vsoc/shm/wifi_exchange_layout.h"
 
+#include "uapi/vsoc_shm.h"
+
+namespace {
+
+uint32_t AlignToPageSize(uint32_t val) {
+  static uint32_t page_size = sysconf(_SC_PAGESIZE);
+  return ((val + (page_size - 1)) / page_size) * page_size;
+}
+
+uint32_t AlignToPowerOf2(uint32_t val) {
+  uint32_t power_of_2 = 1;
+  while (power_of_2 < val) {
+    power_of_2 *= 2;
+  }
+  return power_of_2;
+}
+
+// Takes a vector of objects and returns a vector of pointers to those objects.
+template <typename T, typename R>
+std::vector<R*> GetConstPointers(const std::vector<T>& v) {
+  std::vector<R*> result;
+  result.reserve(v.size());
+  for (auto& element : v) {
+    result.push_back(&element);
+  }
+  return result;
+}
+}  // namespace
+
 namespace vsoc {
 
 namespace {
+
+class VSoCRegionLayoutImpl : public VSoCRegionLayout {
+ public:
+  VSoCRegionLayoutImpl(const char* region_name, size_t layout_size,
+                       int guest_to_host_signal_table_log_size,
+                       int host_to_guest_signal_table_log_size,
+                       const char* managed_by)
+      : region_name_(region_name),
+        layout_size_(layout_size),
+        guest_to_host_signal_table_log_size_(
+            guest_to_host_signal_table_log_size),
+        host_to_guest_signal_table_log_size_(
+            host_to_guest_signal_table_log_size),
+        managed_by_(managed_by) {
+    size_ = GetMinRegionSize();
+    LOG(INFO) << region_name << ": is " << size_;
+  }
+  VSoCRegionLayoutImpl(const VSoCRegionLayoutImpl&) = default;
+
+  const char* region_name() const override { return region_name_; }
+  const char* managed_by() const override { return managed_by_; }
+
+  size_t layout_size() const override { return layout_size_; }
+  int guest_to_host_signal_table_log_size() const override {
+    return guest_to_host_signal_table_log_size_;
+  }
+  int host_to_guest_signal_table_log_size() const override {
+    return host_to_guest_signal_table_log_size_;
+  }
+  uint32_t begin_offset() const override { return begin_offset_; }
+  size_t region_size() const override { return size_; }
+  void SetRegionSize(size_t size) { size_ = size; }
+  void SetBeginOffset(uint32_t offset) { begin_offset_ = offset; }
+
+  // Returns the minimum size the region needs to accomodate the signaling
+  // section and the data layout.
+  size_t GetMinRegionSize() const {
+    auto size = GetOffsetOfRegionData();
+    // Data section
+    size += layout_size_;
+    size = AlignToPageSize(size);
+    return size;
+  }
+
+  uint32_t GetOffsetOfRegionData() const {
+    uint32_t offset = 0;
+    // Signal tables
+    offset += (1 << guest_to_host_signal_table_log_size_) * sizeof(uint32_t);
+    offset += (1 << host_to_guest_signal_table_log_size_) * sizeof(uint32_t);
+    // Interrup signals
+    offset += 2 * sizeof(uint32_t);
+    return offset;
+  }
+
+ private:
+  const char* region_name_{};
+  const size_t layout_size_{};
+  const int guest_to_host_signal_table_log_size_{};
+  const int host_to_guest_signal_table_log_size_{};
+  const char* managed_by_{};
+  uint32_t begin_offset_{};
+  size_t size_{};
+};
+
+class VSoCMemoryLayoutImpl : public VSoCMemoryLayout {
+ public:
+  explicit VSoCMemoryLayoutImpl(std::vector<VSoCRegionLayoutImpl>&& regions)
+      : regions_(regions), region_idx_by_name_(GetNameToIndexMap(regions)) {
+    for (size_t i = 0; i < regions_.size(); ++i) {
+      // This link could be resolved later, but doing it here disables
+      // managed_by cycles among the regions.
+      if (regions[i].managed_by() &&
+          !region_idx_by_name_.count(regions[i].managed_by())) {
+        LOG(FATAL) << regions[i].region_name()
+                   << " managed by unknown region: " << regions[i].managed_by()
+                   << ". Manager Regions must be declared before the regions "
+                      "they manage";
+      }
+    }
+
+    uint32_t offset = 0;
+    // Reserve space for global header
+    offset += sizeof(vsoc_shm_layout_descriptor);
+    // and region descriptors
+    offset += regions_.size() * sizeof(vsoc_device_region);
+    offset = AlignToPageSize(offset);
+
+    // Calculate offsets for all regions and set the size of the device
+    UpdateRegionOffsetsAndDeviceSize(offset);
+  }
+
+  ~VSoCMemoryLayoutImpl() = default;
+
+  std::vector<const VSoCRegionLayout*> GetRegions() const {
+    static std::vector<const VSoCRegionLayout*> ret =
+        GetConstPointers<VSoCRegionLayoutImpl, const VSoCRegionLayout>(
+            regions_);
+    return ret;
+  }
+
+  const VSoCRegionLayout* GetRegionByName(
+      const char* region_name) const override {
+    if (!region_idx_by_name_.count(region_name)) {
+      return nullptr;
+    }
+    return &regions_[region_idx_by_name_.at(region_name)];
+  }
+
+  uint32_t GetMemoryFileSize() const override { return device_size_; }
+
+  void WriteLayout(void* shared_memory) const override;
+
+  bool ResizeRegion(const char* region_name, size_t new_min_size) override {
+    if (!region_idx_by_name_.count(region_name)) {
+      LOG(ERROR) << "Unable to resize region: " << region_name
+                 << ". Region not found";
+      return false;
+    }
+    auto index = region_idx_by_name_.at(region_name);
+    auto& region = regions_[index];
+    auto min_required_size = region.GetMinRegionSize();
+
+    // Align to page size
+    new_min_size = AlignToPageSize(new_min_size);
+    if (new_min_size < min_required_size) {
+      LOG(ERROR) << "Requested resize of region " << region_name << " to "
+                 << new_min_size << " (after alignment), it needs at least "
+                 << min_required_size << " bytes.";
+      return false;
+    }
+
+    region.SetRegionSize(new_min_size);
+
+    // Get new offset for next region
+    auto offset = region.begin_offset() + region.region_size();
+    // Update offsets for all following regions
+    UpdateRegionOffsetsAndDeviceSize(offset, index + 1);
+    return true;
+  }
+
+ protected:
+  VSoCMemoryLayoutImpl() = delete;
+  VSoCMemoryLayoutImpl(const VSoCMemoryLayoutImpl&) = delete;
+
+  // Helper function to allow the creation of the name to index map in the
+  // constructor and allow the field to be const
+  static std::map<const char*, size_t> GetNameToIndexMap(
+      const std::vector<VSoCRegionLayoutImpl>& regions) {
+    std::map<const char*, size_t> result;
+    for (size_t index = 0; index < regions.size(); ++index) {
+      auto region_name = regions[index].region_name();
+      if (result.count(region_name)) {
+        LOG(FATAL) << region_name << " used for more than one region";
+      }
+      result[region_name] = index;
+    }
+    return result;
+  }
+
+  // Updates the beginning offset of all regions starting at a specific index
+  // (useful after a resize operation) and the device's size.
+  void UpdateRegionOffsetsAndDeviceSize(uint32_t offset, size_t index = 0) {
+    for (; index < regions_.size(); ++index) {
+      regions_[index].SetBeginOffset(offset);
+      offset += regions_[index].region_size();
+    }
+
+    // Make the device's size the smaller power of two possible
+    device_size_ = AlignToPowerOf2(offset);
+  }
+
+  std::vector<VSoCRegionLayoutImpl> regions_;
+  const std::map<const char*, size_t> region_idx_by_name_;
+  uint32_t device_size_{};
+};
+
+// Writes a region's signal table layout to shared memory. Returns the region
+// offset of free memory after the table and interrupt signaled word.
+uint32_t WriteSignalTableDescription(vsoc_signal_table_layout* layout,
+                                     uint32_t offset, int log_size) {
+  layout->num_nodes_lg2 = log_size;
+  // First the signal table
+  layout->futex_uaddr_table_offset = offset;
+  offset += (1 << log_size) * sizeof(uint32_t);
+  // Then the interrupt signaled word
+  layout->interrupt_signalled_offset = offset;
+  offset += sizeof(uint32_t);
+  return offset;
+}
+
+// Writes a region's layout description to shared memory
+void WriteRegionDescription(vsoc_device_region* shmem_region_desc,
+                            const VSoCRegionLayoutImpl& region) {
+  // Region versions are deprecated, write some sensible value
+  shmem_region_desc->current_version = 0;
+  shmem_region_desc->min_compatible_version = 0;
+
+  shmem_region_desc->region_begin_offset = region.begin_offset();
+  shmem_region_desc->region_end_offset =
+      region.begin_offset() + region.region_size();
+  shmem_region_desc->offset_of_region_data = region.GetOffsetOfRegionData();
+  strncpy(shmem_region_desc->device_name, region.region_name(),
+          VSOC_DEVICE_NAME_SZ - 1);
+  shmem_region_desc->device_name[VSOC_DEVICE_NAME_SZ - 1] = '\0';
+  // Guest to host signal table at the beginning of the region
+  uint32_t offset = 0;
+  offset = WriteSignalTableDescription(
+      &shmem_region_desc->guest_to_host_signal_table, offset,
+      region.guest_to_host_signal_table_log_size());
+  // Host to guest signal table right after
+  offset = WriteSignalTableDescription(
+      &shmem_region_desc->host_to_guest_signal_table, offset,
+      region.host_to_guest_signal_table_log_size());
+  // Double check that the region metadata does not collide with the data
+  if (offset > shmem_region_desc->offset_of_region_data) {
+    LOG(FATAL) << "Error: Offset of region data too small (is "
+               << shmem_region_desc->offset_of_region_data << " should be "
+               << offset << " ) for region " << region.region_name()
+               << ". This is a bug";
+  }
+}
+
+void VSoCMemoryLayoutImpl::WriteLayout(void* shared_memory) const {
+  // Device header
+  static_assert(CURRENT_VSOC_LAYOUT_MAJOR_VERSION == 2,
+                "Region layout code must be updated");
+  auto header = reinterpret_cast<vsoc_shm_layout_descriptor*>(shared_memory);
+  header->major_version = CURRENT_VSOC_LAYOUT_MAJOR_VERSION;
+  header->minor_version = CURRENT_VSOC_LAYOUT_MINOR_VERSION;
+  header->size = GetMemoryFileSize();
+  header->region_count = regions_.size();
+
+  // Region descriptions go right after the layout descriptor
+  header->vsoc_region_desc_offset = sizeof(vsoc_shm_layout_descriptor);
+  auto region_descriptions = reinterpret_cast<vsoc_device_region*>(header + 1);
+  for (size_t idx = 0; idx < regions_.size(); ++idx) {
+    auto shmem_region_desc = &region_descriptions[idx];
+    const auto& region = regions_[idx];
+    WriteRegionDescription(shmem_region_desc, region);
+    // Handle managed_by links
+    if (region.managed_by()) {
+      auto manager_idx = region_idx_by_name_.at(region.managed_by());
+      if (manager_idx == VSOC_REGION_WHOLE) {
+        LOG(FATAL) << "Region '" << region.region_name() << "' has owner "
+                   << region.managed_by() << " with index " << manager_idx
+                   << " which is the default value for regions without an "
+                      "owner. Choose a different region to be at index "
+                   << manager_idx
+                   << ", make sure the chosen region is NOT the owner of any "
+                      "other region";
+      }
+      shmem_region_desc->managed_by = manager_idx;
+    } else {
+      shmem_region_desc->managed_by = VSOC_REGION_WHOLE;
+    }
+  }
+}
+
 template <class R>
-RegionMemoryLayout ValidateAndBuildLayout(int region_size,
-                                          int g_to_h_signal_table_log_size,
-                                          int h_to_g_signal_table_log_size,
-                                          const char* managed_by = nullptr) {
+VSoCRegionLayoutImpl ValidateAndBuildLayout(int g_to_h_signal_table_log_size,
+                                            int h_to_g_signal_table_log_size,
+                                            const char* managed_by = nullptr) {
   // Double check that the Layout is a valid shm type.
   ASSERT_SHM_COMPATIBLE(R);
-  return RegionMemoryLayout(R::region_name, region_size,
-                            g_to_h_signal_table_log_size,
-                            h_to_g_signal_table_log_size, managed_by);
+  return VSoCRegionLayoutImpl(R::region_name, sizeof(R),
+                              g_to_h_signal_table_log_size,
+                              h_to_g_signal_table_log_size, managed_by);
 }
 
 }  // namespace
 
-const std::vector<RegionMemoryLayout>& GetVsocMemoryLayout() {
-  static const std::vector<RegionMemoryLayout> layout = {
-      ValidateAndBuildLayout<layout::input_events::InputEventsLayout>(
-          /*size*/ 4096, /*g->h*/ 2, /*h->g*/ 2),
-      ValidateAndBuildLayout<layout::screen::ScreenLayout>(
-          /*size*/ 12292096, /*g->h*/ 2, /*h->g*/ 2),
-      ValidateAndBuildLayout<layout::gralloc::GrallocManagerLayout>(
-          /*size*/ 40960, /*g->h*/ 2, /*h->g*/ 2),
-      ValidateAndBuildLayout<layout::gralloc::GrallocBufferLayout>(
-          /*size*/ 407142400, /*g->h*/ 0, /*h->g*/ 0,
-          /* managed_by */ layout::gralloc::GrallocManagerLayout::region_name),
-      ValidateAndBuildLayout<layout::socket_forward::SocketForwardLayout>(
-          /*size*/ 2105344, /*g->h*/ 7, /*h->g*/ 7),
-      ValidateAndBuildLayout<layout::wifi::WifiExchangeLayout>(
-          /*size*/ 139264, /*g->h*/ 2, /*h->g*/ 2),
-      ValidateAndBuildLayout<layout::ril::RilLayout>(
-          /*size*/ 4096, /*g->h*/ 2, /*h->g*/ 2),
-      ValidateAndBuildLayout<layout::e2e_test::E2EPrimaryTestRegionLayout>(
-          /*size*/ 16384, /*g->h*/ 1, /*h->g*/ 1),
-      ValidateAndBuildLayout<layout::e2e_test::E2ESecondaryTestRegionLayout>(
-          /*size*/ 16384, /*g->h*/ 1, /*h->g*/ 1),
-      ValidateAndBuildLayout<layout::e2e_test::E2EManagerTestRegionLayout>(
-          /*size*/ 4096, /*g->h*/ 1, /*h->g*/ 1),
-      ValidateAndBuildLayout<layout::e2e_test::E2EManagedTestRegionLayout>(
-          /*size*/ 16384, /*g->h*/ 1, /*h->g*/ 1),
-      ValidateAndBuildLayout<layout::audio_data::AudioDataLayout>(
-          /*size*/ 20480, /*g->h*/ 2, /*h->g*/ 2)};
+VSoCMemoryLayout* VSoCMemoryLayout::Get() {
+  /*******************************************************************
+   * Make sure the first region is not the manager of other regions. *
+   *       This error will only be caught on runtime!!!!!            *
+   *******************************************************************/
+  static VSoCMemoryLayoutImpl layout(
+      {ValidateAndBuildLayout<layout::input_events::InputEventsLayout>(2, 2),
+       ValidateAndBuildLayout<layout::screen::ScreenLayout>(2, 2),
+       ValidateAndBuildLayout<layout::gralloc::GrallocManagerLayout>(2, 2),
+       ValidateAndBuildLayout<layout::gralloc::GrallocBufferLayout>(
+           0, 0,
+           /* managed_by */ layout::gralloc::GrallocManagerLayout::region_name),
+       ValidateAndBuildLayout<layout::socket_forward::SocketForwardLayout>(7,
+                                                                           7),
+       ValidateAndBuildLayout<layout::wifi::WifiExchangeLayout>(2, 2),
+       ValidateAndBuildLayout<layout::ril::RilLayout>(2, 2),
+       ValidateAndBuildLayout<layout::e2e_test::E2EPrimaryTestRegionLayout>(1,
+                                                                            1),
+       ValidateAndBuildLayout<layout::e2e_test::E2ESecondaryTestRegionLayout>(
+           1, 1),
+       ValidateAndBuildLayout<layout::e2e_test::E2EManagerTestRegionLayout>(1,
+                                                                            1),
+       ValidateAndBuildLayout<layout::e2e_test::E2EManagedTestRegionLayout>(1,
+                                                                            1),
+       ValidateAndBuildLayout<layout::audio_data::AudioDataLayout>(2, 2)});
 
-  return layout;
+  // We need this code to compile on both sides to enforce the static checks,
+  // but should only be used host side if for no other reason because of the
+  // possible resizing of some regions not being visible on the guest.
+#if !defined(CUTTLEFISH_HOST)
+  LOG(FATAL) << "Memory layout is not accurate in guest side, use region "
+                "classes or the vsoc driver directly instead.";
+#endif
+  return &layout;
 }
 
 }  // namespace vsoc
diff --git a/common/vsoc/lib/vsoc_memory.h b/common/vsoc/lib/vsoc_memory.h
index b6844b8..011e693 100644
--- a/common/vsoc/lib/vsoc_memory.h
+++ b/common/vsoc/lib/vsoc_memory.h
@@ -15,44 +15,55 @@
  * limitations under the License.
  */
 
+#include <stdint.h>
+
 #include <vector>
 
 namespace vsoc {
 
-class RegionMemoryLayout {
+class VSoCRegionLayout {
  public:
-  RegionMemoryLayout(const char* region_name, size_t region_size,
-                     int guest_to_host_signal_table_log_size,
-                     int host_to_guest_signal_table_log_size,
-                     const char* managed_by)
-      : region_name_(region_name),
-        region_size_(region_size),
-        guest_to_host_signal_table_log_size_(
-            guest_to_host_signal_table_log_size),
-        host_to_guest_signal_table_log_size_(
-            host_to_guest_signal_table_log_size),
-        managed_by_(managed_by) {}
+  virtual const char* region_name() const = 0;
+  virtual const char* managed_by() const = 0;
 
-  const char* region_name() const { return region_name_; }
-  size_t region_size() const { return region_size_; }
-  int guest_to_host_signal_table_log_size() const {
-    return guest_to_host_signal_table_log_size_;
-  }
-  int host_to_guest_signal_table_log_size() const {
-    return host_to_guest_signal_table_log_size_;
-  }
-  const char * managed_by() const {
-    return managed_by_;
-  }
-
- private:
-  const char* region_name_{};
-  size_t region_size_{};
-  int guest_to_host_signal_table_log_size_{};
-  int host_to_guest_signal_table_log_size_{};
-  const char* managed_by_{};
+  virtual size_t layout_size() const = 0;
+  virtual int guest_to_host_signal_table_log_size() const = 0;
+  virtual int host_to_guest_signal_table_log_size() const = 0;
+  virtual uint32_t begin_offset() const = 0;
+  virtual size_t region_size() const = 0;
+ protected:
+  VSoCRegionLayout() = default;
+  virtual ~VSoCRegionLayout() = default;
 };
 
-const std::vector<RegionMemoryLayout>& GetVsocMemoryLayout();
+class VSoCMemoryLayout {
+ public:
+  // Returns a pointer to the memory layout singleton.
+  static VSoCMemoryLayout* Get();
+
+  VSoCMemoryLayout(const VSoCMemoryLayout&) = delete;
+  VSoCMemoryLayout(VSoCMemoryLayout&&) = delete;
+
+  virtual std::vector<const VSoCRegionLayout *> GetRegions() const = 0;
+  virtual const VSoCRegionLayout* GetRegionByName(
+      const char* region_name) const = 0;
+
+  // Returns the smallest number of bytes that is a power of 2 and can
+  // accomodate the entire memory layout.
+  virtual uint32_t GetMemoryFileSize() const = 0;
+
+  // Writes the layout to memory. shared_memory should be the result of mmap-ing
+  // a file of at least GetMemoryFileSize() bytes.
+  virtual void WriteLayout(void * shared_memory) const = 0;
+
+  // Increase the size of the data section of a region to make the entire region
+  // at least new_min_size bytes. The actual region size will be no less than
+  // the minimum size required to accomodate the signal tables and the memory
+  // layout, and will be aligned to page size.
+  virtual bool ResizeRegion(const char* region_name, size_t new_min_size) = 0;
+ protected:
+  VSoCMemoryLayout() = default;
+  virtual ~VSoCMemoryLayout() = default;
+};
 
 }  // namespace vsoc
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
index 15a53f1..c3b2151 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -36,6 +36,7 @@
 #include "common/libs/fs/shared_select.h"
 #include "common/libs/strings/str_split.h"
 #include "common/vsoc/lib/vsoc_memory.h"
+#include "common/vsoc/shm/screen_layout.h"
 #include "host/commands/launch/pre_launch_initializers.h"
 #include "host/libs/config/file_partition.h"
 #include "host/libs/config/guest_config.h"
@@ -70,7 +71,12 @@
              "The size of the blank data image to generate, MB.");
 DEFINE_string(blank_data_image_fmt, "ext4",
               "The fs format for the blank data image. Used with mkfs.");
+
 DECLARE_int32(dpi);
+DECLARE_int32(x_res);
+DECLARE_int32(y_res);
+DECLARE_int32(num_screen_buffers);
+
 DEFINE_bool(disable_app_armor_security, false,
             "Disable AppArmor security in libvirt. For debug only.");
 DEFINE_bool(disable_dac_security, false,
@@ -488,6 +494,7 @@
   }
 
   std::string entropy_source = "/dev/urandom";
+  auto& memory_layout = *vsoc::VSoCMemoryLayout::Get();
 
   config::GuestConfig cfg;
   cfg.SetID(FLAGS_instance)
@@ -497,7 +504,7 @@
       .SetInitRDName(FLAGS_initrd)
       .SetKernelArgs(cmdline.str())
       .SetIVShMemSocketPath(FLAGS_qemusocket)
-      .SetIVShMemVectorCount(vsoc::GetVsocMemoryLayout().size())
+      .SetIVShMemVectorCount(memory_layout.GetRegions().size())
       .SetSystemPartitionPath(system_partition->GetName())
       .SetCachePartitionPath(cache_partition->GetName())
       .SetDataPartitionPath(data_partition->GetName())
@@ -522,6 +529,22 @@
   VirtualUSBManager vadb(cfg.GetUSBV1SocketName(), FLAGS_vhci_port,
                          GetPerInstanceDefault("android_usbip"));
   vadb.Start();
+
+  // TODO(b/79170615) These values need to go to the config object/file and the
+  // region resizing be done by the ivserver process (or maybe the config
+  // library to ensure all processes have the correct value?)
+  size_t screen_size =
+      memory_layout
+          .GetRegionByName(vsoc::layout::screen::ScreenLayout::region_name)
+          ->region_size();
+  auto actual_width = ((FLAGS_x_res * 4) + 15) & ~15;  // aligned to 16
+  screen_size += FLAGS_num_screen_buffers *
+                 (actual_width * FLAGS_y_res + 16 /* padding */);
+  screen_size += (FLAGS_num_screen_buffers - 1) * 4096; /* Guard pages */
+  memory_layout.ResizeRegion(vsoc::layout::screen::ScreenLayout::region_name,
+                             screen_size);
+  // TODO(b/79170615) Resize gralloc region too.
+
   IVServerManager ivshmem;
   ivshmem.Start();
   KernelLogMonitor kmon(cfg.GetKernelLogSocketName(),
diff --git a/host/commands/launch/screen_region_handler.cc b/host/commands/launch/screen_region_handler.cc
index 5844537..4214482 100644
--- a/host/commands/launch/screen_region_handler.cc
+++ b/host/commands/launch/screen_region_handler.cc
@@ -25,12 +25,13 @@
 DEFINE_int32(y_res, 1280, "Height of the screen in pixels");
 DEFINE_int32(dpi, 160, "Pixels per inch for the screen");
 DEFINE_int32(refresh_rate_hz, 60, "Screen refresh rate in Hertz");
+DEFINE_int32(num_screen_buffers, 3, "The number of screen buffers");
 
 void InitializeScreenRegion() {
   auto region =
       vsoc::screen::ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
   if (!region) {
-    LOG(ERROR) << "Framebuffer region was not found";
+    LOG(ERROR) << "Screen region was not found";
     return;
   }
   auto dest = region->data();
diff --git a/host/libs/ivserver/qemu_client.cc b/host/libs/ivserver/qemu_client.cc
index 2771e06..e3a5cde 100644
--- a/host/libs/ivserver/qemu_client.cc
+++ b/host/libs/ivserver/qemu_client.cc
@@ -81,7 +81,7 @@
   for (const auto region_data : shmem.Regions()) {
     if (!SendSocketInfo(QemuFDMsg::kHostSideHald, region_data.host_fd)) {
       LOG(ERROR) << "Failed to send Host Side FD for region "
-                 << region_data.values.device_name << ": " << client_socket_->StrError();
+                 << region_data.device_name << ": " << client_socket_->StrError();
       return false;
     }
   }
@@ -95,7 +95,7 @@
   for (const auto region_data : shmem.Regions()) {
     if (!SendSocketInfo(QemuFDMsg::kGuestSideHal, region_data.guest_fd)) {
       LOG(ERROR) << "Failed to send Guest Side FD for region "
-                 << region_data.values.device_name << ": " << client_socket_->StrError();
+                 << region_data.device_name << ": " << client_socket_->StrError();
       return false;
     }
   }
diff --git a/host/libs/ivserver/vsocsharedmem.cc b/host/libs/ivserver/vsocsharedmem.cc
index f23d972..080ad20 100644
--- a/host/libs/ivserver/vsocsharedmem.cc
+++ b/host/libs/ivserver/vsocsharedmem.cc
@@ -35,53 +35,9 @@
 namespace ivserver {
 namespace {
 
-static_assert(CURRENT_VSOC_LAYOUT_MAJOR_VERSION == 2,
-              "Region layout code must be updated");
-
-class RegionAllocator {
- public:
-  static uint32_t PageSize() {
-    static long page_size = sysconf(_SC_PAGESIZE);
-    return page_size;
-  }
-
-  explicit RegionAllocator(const std::string &name, uint32_t max_size,
-                           uint32_t offset = 0)
-      : name_{name}, max_size_{max_size}, offset_{offset} {}
-
-  uint32_t Allocate(uint32_t size, const char *usage, bool *error) {
-    if (size > (max_size_ - offset_)) {
-      *error = true;
-      LOG(ERROR) << name_ << ":"
-                 << "allocation of " << size << "bytes for " << usage
-                 << " will overflow memory region";
-    }
-
-    offset_ += size;
-    return (offset_ - size);
-  }
-
-  void PadTo(uint32_t size, bool *error) {
-    uint32_t padding = ((offset_ + (size - 1)) / size) * size - offset_;
-    Allocate(padding, "padding", error);
-  }
-
-  uint32_t AllocateRest(bool *error) {
-    return Allocate(max_size_ - offset_, "rest of region", error);
-  }
-
-  uint32_t GetOffset() const { return offset_; }
-
- private:
-  std::string name_;
-  uint32_t max_size_;
-  uint32_t offset_;
-};
-
 class VSoCSharedMemoryImpl : public VSoCSharedMemory {
  public:
-  VSoCSharedMemoryImpl(const vsoc_shm_layout_descriptor &header,
-                       const std::map<std::string, size_t> &name_to_region_idx,
+  VSoCSharedMemoryImpl(const std::map<std::string, size_t> &name_to_region_idx,
                        const std::vector<Region> &regions,
                        const std::string &path);
 
@@ -96,7 +52,6 @@
  private:
   void CreateLayout();
 
-  const vsoc_shm_layout_descriptor &header_;
   cvd::SharedFD shared_mem_fd_;
   const std::map<std::string, size_t> region_name_to_index_;
   const std::vector<Region> region_data_;
@@ -106,11 +61,9 @@
 };
 
 VSoCSharedMemoryImpl::VSoCSharedMemoryImpl(
-    const vsoc_shm_layout_descriptor &header,
     const std::map<std::string, size_t> &name_to_region_idx,
     const std::vector<Region> &regions, const std::string &path)
-    : header_{header},
-      region_name_to_index_{name_to_region_idx},
+    : region_name_to_index_{name_to_region_idx},
       region_data_{regions} {
   // TODO(ender): Lock the file after creation and check lock status upon second
   // execution attempt instead of throwing an error.
@@ -122,7 +75,8 @@
   LOG_IF(FATAL, !shared_mem_fd_->IsOpen())
       << "Error in creating shared_memory file: " << shared_mem_fd_->StrError();
 
-  int truncate_res = shared_mem_fd_->Truncate(header_.size);
+  int truncate_res = shared_mem_fd_->Truncate(
+      vsoc::VSoCMemoryLayout::Get()->GetMemoryFileSize());
   LOG_IF(FATAL, truncate_res == -1)
       << "Error in sizing up the shared memory file: "
       << shared_mem_fd_->StrError();
@@ -139,18 +93,13 @@
 }
 
 void VSoCSharedMemoryImpl::CreateLayout() {
-  void *mmap_addr = shared_mem_fd_->Mmap(0, header_.size,
-                                         PROT_READ | PROT_WRITE, MAP_SHARED, 0);
+  auto mmap_length = vsoc::VSoCMemoryLayout::Get()->GetMemoryFileSize();
+  void *mmap_addr = shared_mem_fd_->Mmap(0, mmap_length, PROT_READ | PROT_WRITE,
+                                         MAP_SHARED, 0);
   LOG_IF(FATAL, mmap_addr == MAP_FAILED)
       << "Error mmaping file: " << strerror(errno);
-  *reinterpret_cast<vsoc_shm_layout_descriptor *>(mmap_addr) = header_;
-  auto region_dest = reinterpret_cast<vsoc_device_region *>(
-      reinterpret_cast<uintptr_t>(mmap_addr) + header_.vsoc_region_desc_offset);
-
-  for (const auto &region : region_data_) {
-    *region_dest++ = region.values;
-  }
-  munmap(mmap_addr, header_.size);
+  vsoc::VSoCMemoryLayout::Get()->WriteLayout(mmap_addr);
+  munmap(mmap_addr, mmap_length);
 }
 
 bool VSoCSharedMemoryImpl::GetEventFdPairForRegion(
@@ -164,146 +113,42 @@
   return true;
 }
 
-std::shared_ptr<VSoCSharedMemory::Region> SpecToRegion(
-    const std::string &region_name, const vsoc::RegionMemoryLayout &region_spec,
-    bool *failed) {
-  std::shared_ptr<VSoCSharedMemory::Region> region(
-      new VSoCSharedMemory::Region);
-  // This is obsolete, but the vsoc driver still uses it, so set it to safe
-  // values
-  region->values.current_version = 0;
-  region->values.min_compatible_version = 0;
-
-  region->values.region_end_offset = region_spec.region_size();
-  RegionAllocator allocator(region_name, region->values.region_end_offset);
-
-  // Signal tables
-  vsoc_signal_table_layout *dest = &region->values.guest_to_host_signal_table;
-  dest->num_nodes_lg2 = region_spec.guest_to_host_signal_table_log_size();
-  dest->futex_uaddr_table_offset = allocator.Allocate(
-      (1 << dest->num_nodes_lg2) * sizeof(uint32_t), "node table", failed);
-  dest->interrupt_signalled_offset =
-      allocator.Allocate(sizeof(uint32_t), "signal word", failed);
-
-  dest = &region->values.host_to_guest_signal_table;
-  dest->num_nodes_lg2 = region_spec.host_to_guest_signal_table_log_size();
-  dest->futex_uaddr_table_offset = allocator.Allocate(
-      (1 << dest->num_nodes_lg2) * sizeof(uint32_t), "node table", failed);
-  dest->interrupt_signalled_offset =
-      allocator.Allocate(sizeof(uint32_t), "signal word", failed);
-
-  region->values.offset_of_region_data = allocator.AllocateRest(failed);
-  return region;
-}
-
 }  // anonymous namespace
 
 std::unique_ptr<VSoCSharedMemory> VSoCSharedMemory::New(
     const std::string &path) {
-  auto& device_regions = vsoc::GetVsocMemoryLayout();
-  bool failed = false;
-  RegionAllocator shm_file("shared_memory_file", UINT32_MAX);
-  vsoc_shm_layout_descriptor header{};
-  header.major_version = CURRENT_VSOC_LAYOUT_MAJOR_VERSION;
-  header.minor_version = CURRENT_VSOC_LAYOUT_MINOR_VERSION;
-  // size is handled at N1
-  header.region_count = device_regions.size();
-  shm_file.Allocate(sizeof(header), "header", &failed);
-  header.vsoc_region_desc_offset =
-      shm_file.Allocate(sizeof(vsoc_device_region) * header.region_count,
-                        "region descriptors", &failed);
-  // Align to a page boundary for the first region
-  shm_file.PadTo(RegionAllocator::PageSize(), &failed);
+  auto device_layout = vsoc::VSoCMemoryLayout::Get();
 
   std::map<std::string, size_t> name_to_region_idx;
   std::vector<Region> regions;
-  regions.reserve(header.region_count);
-  std::map<std::string, std::string> managed_by_references;
+  regions.reserve(device_layout->GetRegions().size());
 
-  // Pass 1: Parse individual region structures validating all of the
-  // fields that can be validated without help.
-  for (const auto &region_spec : device_regions) {
-    const std::string device_name = region_spec.region_name();
-    if (name_to_region_idx.count(device_name)) {
-      LOG(ERROR) << device_name << " used for more than one region";
-      failed = true;
-      continue;
-    }
-    std::shared_ptr<Region> region =
-        SpecToRegion(device_name, region_spec, &failed);
+  for (auto region_spec : device_layout->GetRegions()) {
+    auto device_name = region_spec->region_name();
+
     // Create one pair of eventfds for this region. Note that the guest to host
     // eventfd is non-blocking, whereas the host to guest eventfd is blocking.
     // This is in anticipation of blocking semantics for the host side locks.
-    region->host_fd = cvd::SharedFD::Event(0, EFD_NONBLOCK);
-    if (!region->host_fd->IsOpen()) {
-      failed = true;
+    auto host_fd = cvd::SharedFD::Event(0, EFD_NONBLOCK);
+    if (!host_fd->IsOpen()) {
       LOG(ERROR) << "Failed to create host eventfd for " << device_name << ": "
-                 << region->host_fd->StrError();
+                 << host_fd->StrError();
+      return nullptr;
     }
-    region->guest_fd = cvd::SharedFD::Event(0, EFD_NONBLOCK);
-    if (!region->guest_fd->IsOpen()) {
-      failed = true;
+    auto guest_fd = cvd::SharedFD::Event(0, EFD_NONBLOCK);
+    if (!guest_fd->IsOpen()) {
       LOG(ERROR) << "Failed to create guest eventfd for " << device_name << ": "
-                 << region->guest_fd->StrError();
-    }
-    region->values.region_begin_offset = shm_file.Allocate(
-        region->values.region_end_offset, device_name.c_str(), &failed);
-    shm_file.PadTo(RegionAllocator::PageSize(), &failed);
-    region->values.region_end_offset = shm_file.GetOffset();
-    if (sizeof(region->values.device_name) - device_name.size() < 1) {
-      LOG(ERROR) << device_name << " is too long for a region name";
-      failed = true;
-    } else {
-      strcpy(region->values.device_name, device_name.c_str());
+                 << guest_fd->StrError();
+      return nullptr;
     }
 
     auto region_idx = regions.size();
     name_to_region_idx[device_name] = region_idx;
-    regions.push_back(*region);
+    regions.emplace_back(device_name, host_fd, guest_fd);
+  }
 
-    if (region_spec.managed_by()) {
-      // This forces manager to appear before the managed region, indirectly
-      // forbidding cycles in managed links.
-      if (!name_to_region_idx.count(region_spec.managed_by())) {
-        LOG(ERROR) << device_name << " managed by missing region "
-                   << region_spec.managed_by();
-        failed = true;
-        continue;
-      }
-      auto manager_idx = name_to_region_idx[region_spec.managed_by()];
-      regions[region_idx].values.managed_by = manager_idx;
-      if (manager_idx == VSOC_REGION_WHOLE) {
-        LOG(ERROR) << "Region '" << device_name << "' has owner "
-                   << region_spec.managed_by() << " with index " << manager_idx
-                   << " which is the default value for regions without an "
-                      "owner. Choose a different region to be at index "
-                   << manager_idx
-                   << ", make sure the chosen region is NOT the owner of any "
-                      "other region";
-      }
-    }
-  }
-  if (failed) {
-    return nullptr;
-  }
-  // Handles size (marker N1)
-  // The size must be a power of 2
-  size_t temp = shm_file.GetOffset();
-  size_t allocated_size = 0;
-  // Find the highest set bit
-  while(temp) {
-    allocated_size = temp;
-    // Clear a bit
-    temp = temp & (temp -1);
-  }
-  // If the size is already a power of 2 just use it. Otherswise use the
-  // next higher power of 2
-  if (allocated_size < shm_file.GetOffset()) {
-    allocated_size *= 2;
-  }
-  header.size = allocated_size;
   return std::unique_ptr<VSoCSharedMemory>(
-      new VSoCSharedMemoryImpl(header, name_to_region_idx, regions, path));
+      new VSoCSharedMemoryImpl(name_to_region_idx, regions, path));
 }
 
 }  // namespace ivserver
diff --git a/host/libs/ivserver/vsocsharedmem.h b/host/libs/ivserver/vsocsharedmem.h
index 5610709..613c6a0 100644
--- a/host/libs/ivserver/vsocsharedmem.h
+++ b/host/libs/ivserver/vsocsharedmem.h
@@ -30,7 +30,11 @@
  public:
   // Region describes a VSoCSharedMem region.
   struct Region {
-    vsoc_device_region values{};
+    Region() = default;
+    explicit Region(const char *device_name, const cvd::SharedFD &host_fd,
+                    const cvd::SharedFD &guest_fd)
+        : device_name(device_name), host_fd(host_fd), guest_fd(guest_fd) {}
+    const char *device_name;
     cvd::SharedFD host_fd;
     cvd::SharedFD guest_fd;
   };