#pragma once /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "common/vsoc/shm/base.h" // Memory layout for a region that supports end-to-end (E2E) testing of // shared memory regions. This verifies that all sorts of things work along the // the path: // // host libraries <-> ivshmem server <-> kernel <-> guest libraries // // This is intentionally not a unit test. The primary source of errors along // this path is a misunderstanding and/or inconsistency in one of the // interfaces. Introducing mocks would allow these errors to go undetected. // Another way of looking at it is that the mocks would end up being a // a copy-and-paste job, making a series of change-detector tests. // // These tests are actually run on every device boot to verify that things are // ok. namespace vsoc { namespace layout { namespace e2e_test { /** * Flags that are used to indicate test status. Some of the latter testing * stages rely on initializion that must be done on the peer. */ enum E2ETestStage : uint32_t { // No tests have passed E2E_STAGE_NONE = 0, // This side has finished writing its pattern to the region E2E_MEMORY_FILLED = 1, // This side has confirmed that it can see its peer's writes to the region E2E_PEER_MEMORY_READ = 2, }; static_assert(ShmTypeValidator::valid, "Compilation error. Please fix above errors and retry."); /** * Structure that grants permission to write in the region to either the guest * or the host. This size of these fields is arbitrary. */ struct E2EMemoryFill { static constexpr size_t layout_size = 64; static const std::size_t kOwnedFieldSize = 32; // The compiler must not attempt to optimize away reads and writes to the // shared memory window. This is pretty typical when dealing with devices // doing memory mapped I/O. char host_writable[kOwnedFieldSize]; char guest_writable[kOwnedFieldSize]; }; ASSERT_SHM_COMPATIBLE(E2EMemoryFill); /** * Structure that grants permission to write in the region to either the guest * or the host. This size of these fields is arbitrary. */ class E2ETestStageRegister { public: static constexpr size_t layout_size = 4; E2ETestStage value() const { return value_; } void set_value(E2ETestStage new_value) { value_ = new_value; } protected: // The compiler must not attempt to optimize away reads and writes to the // shared memory window. This is pretty typical when dealing with devices // doing memory mapped I/O. E2ETestStage value_; }; ASSERT_SHM_COMPATIBLE(E2ETestStageRegister); /** * Describes the layout of the regions used for the end-to-end test. There * are multiple regions: primary and secondary, so some details like the region * name must wait until later. */ class E2ETestRegionLayout : public ::vsoc::layout::RegionLayout { public: static constexpr size_t layout_size = 2 * E2ETestStageRegister::layout_size + 3 * 4 + E2EMemoryFill::layout_size; /** * Computes how many E2EMemoryFill records we need to cover the region. * Covering the entire region during the test ensures that everything is * mapped and coherent between guest and host. */ static std::size_t NumFillRecords(std::size_t region_size) { if (region_size < sizeof(E2ETestRegionLayout)) { return 0; } // 1 + ... An array of size 1 is allocated in the E2ETestRegion. // TODO(ghartman): AddressSanitizer may find this sort of thing to be // alarming. return 1 + (region_size - sizeof(E2ETestRegionLayout)) / sizeof(E2EMemoryFill); } // The number of test stages that have completed on the guest // Later host tests will wait on this E2ETestStageRegister guest_status; // The number of test stages that have completed on the host // Later guest tests will wait on this E2ETestStageRegister host_status; // These fields are used to test the signaling mechanism. std::atomic host_to_guest_signal; std::atomic guest_to_host_signal; std::atomic guest_self_register; // There rest of the region will be filled by guest_host_strings. // We actually use more than one of these, but we can't know how many // until we examine the region. E2EMemoryFill data[1]; }; ASSERT_SHM_COMPATIBLE(E2ETestRegionLayout); struct E2EPrimaryTestRegionLayout : public E2ETestRegionLayout { static constexpr size_t layout_size = E2ETestRegionLayout::layout_size; static const char* region_name; static const char guest_pattern[E2EMemoryFill::kOwnedFieldSize]; static const char host_pattern[E2EMemoryFill::kOwnedFieldSize]; }; ASSERT_SHM_COMPATIBLE(E2EPrimaryTestRegionLayout); struct E2ESecondaryTestRegionLayout : public E2ETestRegionLayout { static constexpr size_t layout_size = E2ETestRegionLayout::layout_size; static const char* region_name; static const char guest_pattern[E2EMemoryFill::kOwnedFieldSize]; static const char host_pattern[E2EMemoryFill::kOwnedFieldSize]; }; ASSERT_SHM_COMPATIBLE(E2ESecondaryTestRegionLayout); /** * Defines an end-to-end region with a name that should never be configured. */ struct E2EUnfindableRegionLayout : public E2ETestRegionLayout { static constexpr size_t layout_size = E2ETestRegionLayout::layout_size; static const char* region_name; }; ASSERT_SHM_COMPATIBLE(E2EUnfindableRegionLayout); } // namespace e2e_test } // namespace layout } // namespace vsoc