| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <assert.h> |
| #include <lib/fit/defer.h> |
| #include <lib/zx/bti.h> |
| #include <lib/zx/iommu.h> |
| #include <lib/zx/vmo.h> |
| #include <limits.h> |
| #include <zircon/syscalls/iommu.h> |
| |
| #include <zxtest/zxtest.h> |
| |
| #include "helpers.h" |
| |
| extern "C" __WEAK zx_handle_t get_root_resource(void); |
| |
| namespace { |
| |
| TEST(VmoSliceTestCase, WriteThrough) { |
| // Create parent VMO with 4 pages. |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size() * 4, 0, &vmo)); |
| |
| // Write to our first two pages. |
| uint32_t val = 42; |
| EXPECT_OK(vmo.write(&val, 0, sizeof(val))); |
| EXPECT_OK(vmo.write(&val, zx_system_get_page_size(), sizeof(val))); |
| |
| // Create a child that can see the middle two pages. |
| zx::vmo slice_vmo; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, zx_system_get_page_size(), |
| zx_system_get_page_size() * 2, &slice_vmo)); |
| |
| // The first page in the slice should have the contents we wrote to the parent earlier. |
| EXPECT_OK(slice_vmo.read(&val, 0, sizeof(val))); |
| EXPECT_EQ(val, 42); |
| |
| // Write to the two pages in the slice. The second page is the third page in the parent and |
| // was never written to or allocated previously. After this the parent should contain |
| // [42, 84, 84, unallocated] |
| val = 84; |
| EXPECT_OK(slice_vmo.write(&val, 0, sizeof(val))); |
| EXPECT_OK(slice_vmo.write(&val, zx_system_get_page_size(), sizeof(val))); |
| |
| EXPECT_OK(vmo.read(&val, 0, sizeof(val))); |
| EXPECT_EQ(val, 42); |
| EXPECT_OK(vmo.read(&val, zx_system_get_page_size(), sizeof(val))); |
| EXPECT_EQ(val, 84); |
| EXPECT_OK(vmo.read(&val, zx_system_get_page_size() * 2, sizeof(val))); |
| EXPECT_EQ(val, 84); |
| EXPECT_OK(vmo.read(&val, zx_system_get_page_size() * 3, sizeof(val))); |
| EXPECT_EQ(val, 0); |
| } |
| |
| TEST(VmoSliceTestCase, DecommitParent) { |
| // Create parent VMO and put some data in it. |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo)); |
| |
| uint8_t val = 42; |
| EXPECT_OK(vmo.write(&val, 0, sizeof(val))); |
| |
| // Create the child and check we can see what we wrote in the parent. |
| zx::vmo slice_vmo; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size(), &slice_vmo)); |
| |
| EXPECT_OK(slice_vmo.read(&val, 0, sizeof(val))); |
| EXPECT_EQ(val, 42); |
| |
| // Decommit from the parent should cause the slice to see fresh zero pages. |
| EXPECT_OK(vmo.op_range(ZX_VMO_OP_DECOMMIT, 0, zx_system_get_page_size(), nullptr, 0)); |
| |
| EXPECT_OK(slice_vmo.read(&val, 0, sizeof(val))); |
| EXPECT_EQ(val, 0); |
| } |
| |
| TEST(VmoSliceTestCase, Nested) { |
| // Create parent. |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size() * 2, 0, &vmo)); |
| |
| // Put something in the first page. |
| uint32_t val = 42; |
| EXPECT_OK(vmo.write(&val, 0, sizeof(val))); |
| |
| // Create a child that can see both pages. |
| zx::vmo slice_vmo; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size() * 2, &slice_vmo)); |
| |
| // Create a child of the child. |
| zx::vmo slice_slice_vmo; |
| ASSERT_OK(slice_vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size() * 2, |
| &slice_slice_vmo)); |
| |
| // Check the child of the child sees parent data. |
| EXPECT_OK(slice_slice_vmo.read(&val, 0, sizeof(val))); |
| EXPECT_EQ(val, 42); |
| |
| // Write to child of child and check parent updates. |
| val = 84; |
| EXPECT_OK(slice_slice_vmo.write(&val, 0, sizeof(val))); |
| EXPECT_OK(slice_slice_vmo.write(&val, zx_system_get_page_size(), sizeof(val))); |
| |
| EXPECT_OK(vmo.read(&val, 0, sizeof(val))); |
| EXPECT_EQ(val, 84); |
| EXPECT_OK(vmo.read(&val, zx_system_get_page_size(), sizeof(val))); |
| EXPECT_EQ(val, 84); |
| } |
| |
| TEST(VmoSliceTestCase, NonSlice) { |
| // Create parent. |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size() * 2, ZX_VMO_RESIZABLE, &vmo)); |
| |
| // Creating children that are not strict slices should fail. |
| zx::vmo slice_vmo; |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, |
| vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size() * 3, &slice_vmo)); |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, vmo.create_child(ZX_VMO_CHILD_SLICE, zx_system_get_page_size(), |
| zx_system_get_page_size() * 2, &slice_vmo)); |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, vmo.create_child(ZX_VMO_CHILD_SLICE, zx_system_get_page_size() * 2, |
| zx_system_get_page_size(), &slice_vmo)); |
| EXPECT_EQ(ZX_ERR_OUT_OF_RANGE, vmo.create_child(ZX_VMO_CHILD_SLICE, 0, UINT64_MAX, &slice_vmo)); |
| const uint64_t nearly_int_max = UINT64_MAX - zx_system_get_page_size() + 1; |
| EXPECT_EQ(ZX_ERR_OUT_OF_RANGE, |
| vmo.create_child(ZX_VMO_CHILD_SLICE, 0, nearly_int_max, &slice_vmo)); |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, vmo.create_child(ZX_VMO_CHILD_SLICE, nearly_int_max, |
| zx_system_get_page_size(), &slice_vmo)); |
| EXPECT_EQ(ZX_ERR_OUT_OF_RANGE, |
| vmo.create_child(ZX_VMO_CHILD_SLICE, nearly_int_max, nearly_int_max, &slice_vmo)); |
| EXPECT_EQ(ZX_ERR_OUT_OF_RANGE, |
| vmo.create_child(ZX_VMO_CHILD_SLICE, nearly_int_max, UINT64_MAX, &slice_vmo)); |
| } |
| |
| TEST(VmoSliceTestCase, NonResizable) { |
| // Create a resizable parent. |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), ZX_VMO_RESIZABLE, &vmo)); |
| |
| // Any slice creation should fail. |
| zx::vmo slice_vmo; |
| EXPECT_EQ(ZX_ERR_NOT_SUPPORTED, |
| vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size(), &slice_vmo)); |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, vmo.create_child(ZX_VMO_CHILD_SLICE | ZX_VMO_CHILD_RESIZABLE, 0, |
| zx_system_get_page_size(), &slice_vmo)); |
| |
| // Switch to a correctly non-resizable parent. |
| vmo.reset(); |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo)); |
| |
| // A resizable slice should fail. |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, vmo.create_child(ZX_VMO_CHILD_SLICE | ZX_VMO_CHILD_RESIZABLE, 0, |
| zx_system_get_page_size(), &slice_vmo)); |
| } |
| |
| TEST(VmoSliceTestCase, CommitChild) { |
| // Create parent VMO. |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo)); |
| |
| // Create a child and commit it. |
| zx::vmo slice_vmo; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size(), &slice_vmo)); |
| EXPECT_OK(slice_vmo.op_range(ZX_VMO_OP_COMMIT, 0, zx_system_get_page_size(), nullptr, 0)); |
| |
| // Now write to the child and verify the parent reads the same. |
| uint8_t val = 42; |
| EXPECT_OK(slice_vmo.write(&val, 0, sizeof(val))); |
| EXPECT_OK(vmo.read(&val, 0, sizeof(val))); |
| EXPECT_EQ(val, 42); |
| } |
| |
| TEST(VmoSliceTestCase, DecommitChild) { |
| // Create parent VMO. |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo)); |
| |
| // Write to the parent to commit some pages. |
| uint8_t val = 42; |
| EXPECT_OK(vmo.write(&val, 0, sizeof(val))); |
| |
| // Create a child and decommit. |
| zx::vmo slice_vmo; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size(), &slice_vmo)); |
| EXPECT_OK(slice_vmo.op_range(ZX_VMO_OP_DECOMMIT, 0, zx_system_get_page_size(), nullptr, 0)); |
| |
| // Reading from the parent should result in fresh zeros. |
| // Now write to the child and verify the parent reads the same. |
| EXPECT_OK(vmo.read(&val, 0, sizeof(val))); |
| EXPECT_EQ(val, 0); |
| } |
| |
| TEST(VmoSliceTestCase, ZeroSized) { |
| // Create parent VMO. |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo)); |
| |
| // Create some zero sized children. |
| zx::vmo slice_vmo1; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, 0, 0, &slice_vmo1)); |
| zx::vmo slice_vmo2; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, zx_system_get_page_size(), 0, &slice_vmo2)); |
| |
| // Reading and writing should fail. |
| uint8_t val = 42; |
| EXPECT_EQ(slice_vmo1.read(&val, 0, 1), ZX_ERR_OUT_OF_RANGE); |
| EXPECT_EQ(slice_vmo2.read(&val, 0, 1), ZX_ERR_OUT_OF_RANGE); |
| EXPECT_EQ(slice_vmo1.write(&val, 0, 1), ZX_ERR_OUT_OF_RANGE); |
| EXPECT_EQ(slice_vmo2.write(&val, 0, 1), ZX_ERR_OUT_OF_RANGE); |
| } |
| |
| TEST(VmoSliceTestCase, ChildSliceOfContiguousParentIsContiguous) { |
| if (!get_root_resource) { |
| printf("Root resource not available, skipping\n"); |
| return; |
| } |
| const size_t size = zx_system_get_page_size(); |
| |
| zx::vmo parent_contig_vmo; |
| zx::unowned_resource root_res(get_root_resource()); |
| |
| zx::iommu iommu; |
| zx::bti bti; |
| auto final_bti_check = vmo_test::CreateDeferredBtiCheck(bti); |
| |
| zx_iommu_desc_dummy_t desc; |
| EXPECT_OK(zx::iommu::create(*root_res, ZX_IOMMU_TYPE_DUMMY, &desc, sizeof(desc), &iommu)); |
| bti = vmo_test::CreateNamedBti(iommu, 0, 0xdeadbeef, "ChildSliceOfContiguousParentIsContiguous"); |
| EXPECT_OK(zx::vmo::create_contiguous(bti, size, 0, &parent_contig_vmo)); |
| |
| // Create child slice. |
| zx::vmo child; |
| ASSERT_OK( |
| parent_contig_vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size(), &child)); |
| |
| zx_info_vmo_t info; |
| ASSERT_OK(child.get_info(ZX_INFO_VMO, &info, sizeof(info), nullptr, nullptr)); |
| EXPECT_EQ(info.flags & ZX_INFO_VMO_CONTIGUOUS, ZX_INFO_VMO_CONTIGUOUS); |
| } |
| |
| TEST(VmoSliceTestCase, ParentContiguousVmoStaysPinnedWithoutHandle) { |
| if (!get_root_resource) { |
| printf("Root resource not available, skipping\n"); |
| return; |
| } |
| // Several pages to increase chance of seeing a failure if parent contiguous VMO doesn't stay |
| // pinned. |
| constexpr uint32_t kPageCount = 32; |
| const size_t size = kPageCount * zx_system_get_page_size(); |
| |
| zx::vmo parent_contig_vmo; |
| zx::unowned_resource root_res(get_root_resource()); |
| |
| zx::iommu iommu; |
| zx::bti bti; |
| auto final_bti_check = vmo_test::CreateDeferredBtiCheck(bti); |
| |
| zx_iommu_desc_dummy_t desc; |
| EXPECT_OK(zx::iommu::create(*root_res, ZX_IOMMU_TYPE_DUMMY, &desc, sizeof(desc), &iommu)); |
| bti = |
| vmo_test::CreateNamedBti(iommu, 0, 0xdeadbeef, "ParentContiguousVmoStaysPinnedWithoutHandle"); |
| EXPECT_OK(zx::vmo::create_contiguous(bti, size, 0, &parent_contig_vmo)); |
| |
| zx_paddr_t paddr[kPageCount]; |
| zx::pmt pmt; |
| ASSERT_OK(bti.pin(ZX_BTI_PERM_READ, parent_contig_vmo, 0, size, &paddr[0], kPageCount, &pmt)); |
| EXPECT_OK(pmt.unpin()); |
| |
| // Create child slice. |
| zx::vmo child; |
| ASSERT_OK( |
| parent_contig_vmo.create_child(ZX_VMO_CHILD_SLICE, 0, size, &child)); |
| |
| parent_contig_vmo.reset(); |
| |
| // If the parent handle closing removed the implicit pin_count tally on the parent, then this |
| // ZX_VMO_OP_ZERO will remove pages from the parent contiguous VMO (which would be incorrect). |
| EXPECT_OK( |
| child.op_range(ZX_VMO_OP_ZERO, /*offset=*/0, size, /*buffer=*/nullptr, /*buffer_size=*/0)); |
| |
| // Commit some pages to a different unrelated VMO, to reduce chances of the contiguous parent |
| // getting back same pages which could potentially lead to a false pass. These stay committed |
| // while we're checking the paddr_t(s) we get back from the slice. |
| zx::vmo unrelated_vmo; |
| EXPECT_OK(zx::vmo::create(size, /*options=*/0, &unrelated_vmo)); |
| EXPECT_OK(unrelated_vmo.op_range(ZX_VMO_OP_COMMIT, /*offset=*/0, size, /*buffer=*/nullptr, |
| /*buffer_size=*/0)); |
| |
| zx_paddr_t paddr_slice[kPageCount]; |
| ASSERT_OK(bti.pin(ZX_BTI_PERM_READ, child, 0, size, &paddr_slice[0], kPageCount, &pmt)); |
| EXPECT_OK(pmt.unpin()); |
| |
| for (uint32_t i = 0; i < kPageCount; ++i) { |
| EXPECT_EQ(paddr[i], paddr_slice[i]); |
| } |
| } |
| |
| TEST(VmoSliceTestCase, ZeroChildren) { |
| // Create parent VMO. |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo)); |
| |
| // Currently the parent has no children, so ZX_VMO_ZERO_CHILDREN should be set. |
| zx_signals_t pending; |
| ASSERT_OK(vmo.wait_one(ZX_VMO_ZERO_CHILDREN, zx::time::infinite(), &pending)); |
| ASSERT_EQ(pending & ZX_VMO_ZERO_CHILDREN, ZX_VMO_ZERO_CHILDREN); |
| |
| // Create child slice. |
| zx::vmo child; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size(), &child)); |
| |
| // Currently the parent has one child, so ZX_VMO_ZERO_CHILDREN should be |
| // cleared. Since child VMO creation is synchronous, this signal must already |
| // be clear. |
| ASSERT_EQ(ZX_ERR_TIMED_OUT, |
| vmo.wait_one(ZX_VMO_ZERO_CHILDREN, zx::time::infinite_past(), &pending)); |
| ASSERT_EQ(pending & ZX_VMO_ZERO_CHILDREN, 0); |
| |
| // Close child slice. |
| child.reset(); |
| |
| // Closing the child doesn't strictly guarantee that ZX_VMO_ZERO_CHILDREN is set |
| // immediately, but it should be set very soon if not already. |
| ASSERT_OK(vmo.wait_one(ZX_VMO_ZERO_CHILDREN, zx::time::infinite(), &pending)); |
| ASSERT_EQ(pending & ZX_VMO_ZERO_CHILDREN, ZX_VMO_ZERO_CHILDREN); |
| } |
| |
| TEST(VmoSliceTestCase, ZeroChildrenGrandchildClosedLast) { |
| // Create parent VMO. |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo)); |
| |
| // Currently the parent has no children, so ZX_VMO_ZERO_CHILDREN should be set. |
| zx_signals_t pending; |
| ASSERT_OK(vmo.wait_one(ZX_VMO_ZERO_CHILDREN, zx::time::infinite(), &pending)); |
| ASSERT_EQ(pending & ZX_VMO_ZERO_CHILDREN, ZX_VMO_ZERO_CHILDREN); |
| |
| // Create child slice. |
| zx::vmo child; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size(), &child)); |
| |
| // Currently the parent has one child, so ZX_VMO_ZERO_CHILDREN should be |
| // cleared. Since child VMO creation is synchronous, this signal must already |
| // be clear. |
| ASSERT_EQ(ZX_ERR_TIMED_OUT, |
| vmo.wait_one(ZX_VMO_ZERO_CHILDREN, zx::time::infinite_past(), &pending)); |
| ASSERT_EQ(pending & ZX_VMO_ZERO_CHILDREN, 0); |
| |
| // Create grandchild slice. |
| zx::vmo grandchild; |
| ASSERT_OK(child.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size(), &grandchild)); |
| |
| // Currently the parent has one child and one grandchild, so ZX_VMO_ZERO_CHILDREN should be |
| // cleared. |
| ASSERT_EQ(ZX_ERR_TIMED_OUT, |
| vmo.wait_one(ZX_VMO_ZERO_CHILDREN, zx::time::infinite_past(), &pending)); |
| ASSERT_EQ(pending & ZX_VMO_ZERO_CHILDREN, 0); |
| |
| // Close child slice. Leave grandchild alone. |
| child.reset(); |
| |
| // Currently the parent has one grandchild, so ZX_VMO_ZERO_CHILDREN should be |
| // cleared. |
| ASSERT_EQ(ZX_ERR_TIMED_OUT, |
| vmo.wait_one(ZX_VMO_ZERO_CHILDREN, zx::time::infinite_past(), &pending)); |
| ASSERT_EQ(pending & ZX_VMO_ZERO_CHILDREN, 0); |
| |
| // Close grandchild slice. |
| grandchild.reset(); |
| |
| // Closing the grandchild (last of all direct or indirect children) doesn't strictly guarantee |
| // that ZX_VMO_ZERO_CHILDREN is set immediately, but it should be set very soon if not already. |
| ASSERT_OK(vmo.wait_one(ZX_VMO_ZERO_CHILDREN, zx::time::infinite(), &pending)); |
| ASSERT_EQ(pending & ZX_VMO_ZERO_CHILDREN, ZX_VMO_ZERO_CHILDREN); |
| } |
| |
| TEST(VmoSliceTestCase, CowPageSourceThroughSlices) { |
| // Create parent VMO. |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo)); |
| |
| // Commit the page so it becomes the initial content for future children. |
| ASSERT_OK(vmo.op_range(ZX_VMO_OP_COMMIT, 0, zx_system_get_page_size(), nullptr, 0)); |
| |
| // Create a COW child so that we have a hidden parent as the root page source. |
| zx::vmo cow_child; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SNAPSHOT, 0, zx_system_get_page_size(), &cow_child)); |
| |
| // Now create a slice of the cow_child |
| zx::vmo slice; |
| ASSERT_OK(cow_child.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size(), &slice)); |
| |
| // Now create a cow child of the slice. |
| // Currently this is forbidden and returns ZX_ERR_NOT_SUPPORTED. If it didn't the |
| // cow_child2.write would cause a kernel assertion to trigger. Once bug 36841 is fixed the else |
| // branch can be removed. |
| zx::vmo cow_child2; |
| zx_status_t result = |
| slice.create_child(ZX_VMO_CHILD_SNAPSHOT, 0, zx_system_get_page_size(), &cow_child2); |
| if (result == ZX_OK) { |
| // Attempt to write to this child. This will require propagating page through both hidden and |
| // non hidden VMOs. |
| uint8_t val; |
| EXPECT_OK(cow_child2.write(&val, 0, 1)); |
| } else { |
| EXPECT_EQ(result, ZX_ERR_NOT_SUPPORTED); |
| } |
| } |
| |
| TEST(VmoSliceTestCase, RoundUpSizePhysical) { |
| if (!get_root_resource) { |
| printf("Root resource not available, skipping\n"); |
| return; |
| } |
| const size_t size = zx_system_get_page_size(); |
| |
| zx::vmo parent_contig_vmo; |
| zx::unowned_resource root_res(get_root_resource()); |
| |
| zx::iommu iommu; |
| zx::bti bti; |
| zx_iommu_desc_dummy_t desc; |
| auto final_bti_check = vmo_test::CreateDeferredBtiCheck(bti); |
| |
| EXPECT_OK(zx::iommu::create(*root_res, ZX_IOMMU_TYPE_DUMMY, &desc, sizeof(desc), &iommu)); |
| bti = vmo_test::CreateNamedBti(iommu, 0, 0xdeadbeef, "RoundUpSizePhysical"); |
| EXPECT_OK(zx::vmo::create_contiguous(bti, size, 0, &parent_contig_vmo)); |
| |
| // Create child slice with size < zx_system_get_page_size(), should round up and succeed. |
| zx::vmo child; |
| ASSERT_OK(parent_contig_vmo.create_child(ZX_VMO_CHILD_SLICE, 0, 42, &child)); |
| } |
| |
| TEST(VmoSliceTestCase, RoundUpSize) { |
| // Create parent VMO and put some data in it near the end. |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo)); |
| |
| uint8_t val = 42; |
| EXPECT_OK(vmo.write(&val, zx_system_get_page_size() - 64, sizeof(val))); |
| |
| // Create child slice with size < zx_system_get_page_size(), should round up and succeed. |
| zx::vmo slice_vmo; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, 0, 42, &slice_vmo)); |
| |
| // Should be able to read the data in the rounded up portion. |
| EXPECT_OK(slice_vmo.read(&val, zx_system_get_page_size() - 64, sizeof(val))); |
| EXPECT_EQ(val, 42); |
| } |
| |
| TEST(VmoSliceTestCase, NotCoWType) { |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo)); |
| zx::vmo slice_vmo; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size(), &slice_vmo)); |
| |
| zx_info_vmo info; |
| EXPECT_OK(slice_vmo.get_info(ZX_INFO_VMO, &info, sizeof(info), nullptr, nullptr)); |
| |
| EXPECT_EQ(0, info.flags & ZX_INFO_VMO_IS_COW_CLONE); |
| } |
| |
| TEST(VmoSliceTestCase, Pin) { |
| if (!get_root_resource) { |
| printf("Root resource not available, skipping\n"); |
| return; |
| } |
| |
| zx::unowned_resource root_res(get_root_resource()); |
| |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo)); |
| zx::vmo slice_vmo; |
| ASSERT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size(), &slice_vmo)); |
| |
| zx::iommu iommu; |
| zx::bti bti; |
| zx_iommu_desc_dummy_t desc; |
| auto final_bti_check = vmo_test::CreateDeferredBtiCheck(bti); |
| |
| EXPECT_OK(zx::iommu::create(*root_res, ZX_IOMMU_TYPE_DUMMY, &desc, sizeof(desc), &iommu)); |
| bti = vmo_test::CreateNamedBti(iommu, 0, 0xdeadbeef, "VmoSliceTestCase::Pin"); |
| |
| // Pin the slice, this should block decommits in the parent. |
| zx_paddr_t paddr; |
| zx::pmt pmt; |
| ASSERT_OK(bti.pin(ZX_BTI_PERM_READ, slice_vmo, 0, zx_system_get_page_size(), &paddr, 1, &pmt)); |
| |
| EXPECT_EQ(ZX_ERR_BAD_STATE, |
| vmo.op_range(ZX_VMO_OP_DECOMMIT, 0, zx_system_get_page_size(), nullptr, 0)); |
| |
| EXPECT_OK(pmt.unpin()); |
| } |
| |
| TEST(VmoSliceTestCase, DeepHierarchy) { |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo)); |
| |
| for (int i = 0; i < 1000; i++) { |
| zx::vmo temp; |
| EXPECT_OK(vmo.create_child(ZX_VMO_CHILD_SLICE, 0, zx_system_get_page_size(), &temp)); |
| vmo = std::move(temp); |
| } |
| vmo.reset(); |
| } |
| |
| } // namespace |