Home | History | Annotate | Download | only in shm
      1 #pragma once
      2 /*
      3  * Copyright (C) 2016 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 #include <atomic>
     19 #include <cstdint>
     20 #include "common/vsoc/shm/base.h"
     21 
     22 // Memory layout for a region that supports end-to-end (E2E) testing of
     23 // shared memory regions. This verifies that all sorts of things work along the
     24 // the path:
     25 //
     26 //   host libraries <-> ivshmem server <-> kernel <-> guest libraries
     27 //
     28 // This is intentionally not a unit test. The primary source of errors along
     29 // this path is a misunderstanding and/or inconsistency in one of the
     30 // interfaces. Introducing mocks would allow these errors to go undetected.
     31 // Another way of looking at it is that the mocks would end up being a
     32 // a copy-and-paste job, making a series of change-detector tests.
     33 //
     34 // These tests are actually run on every device boot to verify that things are
     35 // ok.
     36 
     37 namespace vsoc {
     38 namespace layout {
     39 
     40 namespace e2e_test {
     41 
     42 /**
     43  * Flags that are used to indicate test status. Some of the latter testing
     44  * stages rely on initializion that must be done on the peer.
     45  */
     46   enum E2ETestStage : uint32_t {
     47   // No tests have passed
     48   E2E_STAGE_NONE = 0,
     49   // This side has finished writing its pattern to the region
     50   E2E_MEMORY_FILLED = 1,
     51   // This side has confirmed that it can see its peer's writes to the region
     52   E2E_PEER_MEMORY_READ = 2,
     53 };
     54 static_assert(ShmTypeValidator<E2ETestStage, 4>::valid,
     55               "Compilation error. Please fix above errors and retry.");
     56 
     57 /**
     58  * Structure that grants permission to write in the region to either the guest
     59  * or the host. This size of these fields is arbitrary.
     60  */
     61 struct E2EMemoryFill {
     62   static constexpr size_t layout_size = 64;
     63 
     64   static const std::size_t kOwnedFieldSize = 32;
     65 
     66   // The compiler must not attempt to optimize away reads and writes to the
     67   // shared memory window. This is pretty typical when dealing with devices
     68   // doing memory mapped I/O.
     69   char host_writable[kOwnedFieldSize];
     70   char guest_writable[kOwnedFieldSize];
     71 };
     72 ASSERT_SHM_COMPATIBLE(E2EMemoryFill);
     73 
     74 /**
     75  * Structure that grants permission to write in the region to either the guest
     76  * or the host. This size of these fields is arbitrary.
     77  */
     78 class E2ETestStageRegister {
     79  public:
     80   static constexpr size_t layout_size = 4;
     81 
     82   E2ETestStage value() const {
     83     return value_;
     84   }
     85 
     86   void set_value(E2ETestStage new_value) { value_ = new_value; }
     87 
     88  protected:
     89   // The compiler must not attempt to optimize away reads and writes to the
     90   // shared memory window. This is pretty typical when dealing with devices
     91   // doing memory mapped I/O.
     92   E2ETestStage value_;
     93 };
     94 ASSERT_SHM_COMPATIBLE(E2ETestStageRegister);
     95 
     96 /**
     97  * Describes the layout of the regions used for the end-to-end test. There
     98  * are multiple regions: primary and secondary, so some details like the region
     99  * name must wait until later.
    100  */
    101 class E2ETestRegionLayout : public ::vsoc::layout::RegionLayout {
    102  public:
    103   static constexpr size_t layout_size = 2 * E2ETestStageRegister::layout_size +
    104                                         3 * 4 + E2EMemoryFill::layout_size;
    105 
    106   /**
    107    * Computes how many E2EMemoryFill records we need to cover the region.
    108    * Covering the entire region during the test ensures that everything is
    109    * mapped and coherent between guest and host.
    110    */
    111   static std::size_t NumFillRecords(std::size_t region_size) {
    112     if (region_size < sizeof(E2ETestRegionLayout)) {
    113       return 0;
    114     }
    115     // 1 + ... An array of size 1 is allocated in the E2ETestRegion.
    116     // TODO(ghartman): AddressSanitizer may find this sort of thing to be
    117     // alarming.
    118     return 1 +
    119            (region_size - sizeof(E2ETestRegionLayout)) / sizeof(E2EMemoryFill);
    120   }
    121   // The number of test stages that have completed on the guest
    122   // Later host tests will wait on this
    123   E2ETestStageRegister guest_status;
    124   // The number of test stages that have completed on the host
    125   // Later guest tests will wait on this
    126   E2ETestStageRegister host_status;
    127   // These fields are used to test the signaling mechanism.
    128   std::atomic<uint32_t> host_to_guest_signal;
    129   std::atomic<uint32_t> guest_to_host_signal;
    130   std::atomic<uint32_t> guest_self_register;
    131   // There rest of the region will be filled by guest_host_strings.
    132   // We actually use more than one of these, but we can't know how many
    133   // until we examine the region.
    134   E2EMemoryFill data[1];
    135 };
    136 ASSERT_SHM_COMPATIBLE(E2ETestRegionLayout);
    137 
    138 struct E2EPrimaryTestRegionLayout : public E2ETestRegionLayout {
    139   static constexpr size_t layout_size = E2ETestRegionLayout::layout_size;
    140 
    141   static const char* region_name;
    142   static const char guest_pattern[E2EMemoryFill::kOwnedFieldSize];
    143   static const char host_pattern[E2EMemoryFill::kOwnedFieldSize];
    144 };
    145 ASSERT_SHM_COMPATIBLE(E2EPrimaryTestRegionLayout);
    146 
    147 struct E2ESecondaryTestRegionLayout : public E2ETestRegionLayout {
    148   static constexpr size_t layout_size = E2ETestRegionLayout::layout_size;
    149 
    150   static const char* region_name;
    151   static const char guest_pattern[E2EMemoryFill::kOwnedFieldSize];
    152   static const char host_pattern[E2EMemoryFill::kOwnedFieldSize];
    153 };
    154 ASSERT_SHM_COMPATIBLE(E2ESecondaryTestRegionLayout);
    155 
    156 /**
    157  * Defines an end-to-end region with a name that should never be configured.
    158  */
    159 struct E2EUnfindableRegionLayout : public E2ETestRegionLayout {
    160   static constexpr size_t layout_size = E2ETestRegionLayout::layout_size;
    161 
    162   static const char* region_name;
    163 };
    164 ASSERT_SHM_COMPATIBLE(E2EUnfindableRegionLayout);
    165 
    166 struct E2EManagedTestRegionLayout : public RegionLayout {
    167   static constexpr size_t layout_size = 4;
    168 
    169   static const char* region_name;
    170   uint32_t val;  // Not needed, here only to avoid an empty struct.
    171 };
    172 ASSERT_SHM_COMPATIBLE(E2EManagedTestRegionLayout);
    173 
    174 struct E2EManagerTestRegionLayout : public RegionLayout {
    175   static constexpr size_t layout_size = 4 * 4;
    176 
    177   static const char* region_name;
    178   typedef E2EManagedTestRegionLayout ManagedRegion;
    179   uint32_t data[4];  // We don't need more than 4 for the tests
    180 };
    181 ASSERT_SHM_COMPATIBLE(E2EManagerTestRegionLayout);
    182 
    183 }  // namespace e2e_test
    184 }  // namespace layout
    185 }  // namespace vsoc
    186