// Copyright (c) 2013 The Chromium OS 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 "address_mapper.h" #include <algorithm> #include <memory> #include "base/logging.h" #include "base/macros.h" #include "compat/test.h" namespace quipper { namespace { struct Range { uint64_t addr; uint64_t size; uint64_t id; uint64_t base_offset; Range() : addr(0), size(0), id(0), base_offset(0) {} Range(uint64_t addr, uint64_t size, uint64_t id, uint64_t base_offset) : addr(addr), size(size), id(id), base_offset(base_offset) {} bool contains(const uint64_t check_addr) const { return (check_addr >= addr && check_addr < addr + size); } }; // Some address ranges to map. It is important that none of these overlap with // each other, nor are any two of them contiguous. const Range kMapRanges[] = { Range(0xff000000, 0x100000, 0xdeadbeef, 0), Range(0x00a00000, 0x10000, 0xcafebabe, 0), Range(0x0c000000, 0x1000000, 0x900df00d, 0), Range(0x00001000, 0x30000, 0x9000091e, 0), }; // List of real addresses that are not in the above ranges. const uint64_t kAddressesNotInRanges[] = { 0x00000000, 0x00000100, 0x00038000, 0x00088888, 0x00100000, 0x004fffff, 0x00a20000, 0x00cc0000, 0x00ffffff, 0x03e00000, 0x0b000000, 0x0d100000, 0x0fffffff, 0x1fffffff, 0x7ffffff0, 0xdffffff0, 0xfe000000, 0xffffffff, }; // Number of regularly-spaced intervals within a mapped range to test. const int kNumRangeTestIntervals = 8; // A simple test function to convert a real address to a mapped address. // Address ranges in |ranges| are mapped starting at address 0. uint64_t GetMappedAddressFromRanges(const Range* ranges, const unsigned int num_ranges, const uint64_t addr) { unsigned int i; uint64_t mapped_range_addr; for (i = 0, mapped_range_addr = 0; i < num_ranges; mapped_range_addr += ranges[i].size, ++i) { const Range& range = ranges[i]; if (range.contains(addr)) return (addr - range.addr) + mapped_range_addr; } return static_cast<uint64_t>(-1); } } // namespace // The unit test class for AddressMapper. class AddressMapperTest : public ::testing::Test { public: AddressMapperTest() {} ~AddressMapperTest() {} virtual void SetUp() { mapper_.reset(new AddressMapper); } protected: // Maps a range using the AddressMapper and makes sure that it was successful. // Uses all fields of |range|, including id and base_offset. bool MapRange(const Range& range, bool remove_old_mappings) { VLOG(1) << "Mapping range at " << std::hex << range.addr << " with length of " << range.size << " and id " << range.id; return mapper_->MapWithID(range.addr, range.size, range.id, range.base_offset, remove_old_mappings); } // Tests a range that has been mapped. |expected_mapped_addr| is the starting // address that it should have been mapped to. This mapper will test the start // and end addresses of the range, as well as a bunch of addresses inside it. // Also checks lookup of ID and offset. void TestMappedRange(const Range& range, uint64_t expected_mapped_addr) { uint64_t mapped_addr = UINT64_MAX; AddressMapper::MappingList::const_iterator addr_iter; VLOG(1) << "Testing range at " << std::hex << range.addr << " with length of " << std::hex << range.size; // Check address at the beginning of the range and at subsequent intervals. for (int i = 0; i < kNumRangeTestIntervals; ++i) { const uint64_t offset = i * (range.size / kNumRangeTestIntervals); uint64_t addr = range.addr + offset; EXPECT_TRUE(mapper_->GetMappedAddressAndListIterator(addr, &mapped_addr, &addr_iter)); EXPECT_EQ(expected_mapped_addr + offset, mapped_addr); uint64_t mapped_offset; uint64_t mapped_id; mapper_->GetMappedIDAndOffset(addr, addr_iter, &mapped_id, &mapped_offset); EXPECT_EQ(range.base_offset + offset, mapped_offset); EXPECT_EQ(range.id, mapped_id); } // Check address at end of the range. EXPECT_TRUE(mapper_->GetMappedAddressAndListIterator( range.addr + range.size - 1, &mapped_addr, &addr_iter)); EXPECT_EQ(expected_mapped_addr + range.size - 1, mapped_addr); } std::unique_ptr<AddressMapper> mapper_; }; // Map one range at a time and test looking up addresses. TEST_F(AddressMapperTest, MapSingle) { for (const Range& range : kMapRanges) { mapper_.reset(new AddressMapper); ASSERT_TRUE(MapRange(range, false)); EXPECT_EQ(1U, mapper_->GetNumMappedRanges()); TestMappedRange(range, 0); // Check addresses before the mapped range, should be invalid. uint64_t mapped_addr; AddressMapper::MappingList::const_iterator iter; EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator(range.addr - 1, &mapped_addr, &iter)); EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator(range.addr - 0x100, &mapped_addr, &iter)); EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator( range.addr + range.size, &mapped_addr, &iter)); EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator( range.addr + range.size + 0x100, &mapped_addr, &iter)); EXPECT_EQ(range.size, mapper_->GetMaxMappedLength()); } } // Map all the ranges at once and test looking up addresses. TEST_F(AddressMapperTest, MapAll) { uint64_t size_mapped = 0; for (const Range& range : kMapRanges) { ASSERT_TRUE(MapRange(range, false)); size_mapped += range.size; } EXPECT_EQ(arraysize(kMapRanges), mapper_->GetNumMappedRanges()); // Check the max mapped length in quipper space. EXPECT_EQ(size_mapped, mapper_->GetMaxMappedLength()); // For each mapped range, test addresses at the start, middle, and end. // Also test the address right before and after each range. uint64_t mapped_addr; AddressMapper::MappingList::const_iterator iter; for (const Range& range : kMapRanges) { TestMappedRange(range, GetMappedAddressFromRanges( kMapRanges, arraysize(kMapRanges), range.addr)); // Check addresses before and after the mapped range, should be invalid. EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator(range.addr - 1, &mapped_addr, &iter)); EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator(range.addr - 0x100, &mapped_addr, &iter)); EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator( range.addr + range.size, &mapped_addr, &iter)); EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator( range.addr + range.size + 0x100, &mapped_addr, &iter)); } // Test some addresses that are out of these ranges, should not be able to // get mapped addresses. for (uint64_t addr : kAddressesNotInRanges) EXPECT_FALSE( mapper_->GetMappedAddressAndListIterator(addr, &mapped_addr, &iter)); } // Map all the ranges at once and test looking up IDs and offsets. TEST_F(AddressMapperTest, MapAllWithIDsAndOffsets) { for (const Range& range : kMapRanges) { LOG(INFO) << "Mapping range at " << std::hex << range.addr << " with length of " << std::hex << range.size; ASSERT_TRUE(mapper_->MapWithID(range.addr, range.size, range.id, 0, false)); } EXPECT_EQ(arraysize(kMapRanges), mapper_->GetNumMappedRanges()); // For each mapped range, test addresses at the start, middle, and end. // Also test the address right before and after each range. for (const Range& range : kMapRanges) { TestMappedRange(range, GetMappedAddressFromRanges( kMapRanges, arraysize(kMapRanges), range.addr)); } } // Test overlap detection. TEST_F(AddressMapperTest, OverlapSimple) { // Map all the ranges first. for (const Range& range : kMapRanges) ASSERT_TRUE(MapRange(range, false)); // Attempt to re-map each range, but offset by size / 2. for (const Range& range : kMapRanges) { Range new_range; new_range.addr = range.addr + range.size / 2; new_range.size = range.size; // The maps should fail because of overlap with an existing mapping. EXPECT_FALSE(MapRange(new_range, false)); } // Re-map each range with the same offset. Only this time, remove any old // mapped range that overlaps with it. for (const Range& range : kMapRanges) { Range new_range; new_range.addr = range.addr + range.size / 2; new_range.size = range.size; EXPECT_TRUE(MapRange(new_range, true)); // Make sure the number of ranges is unchanged (one deleted, one added). EXPECT_EQ(arraysize(kMapRanges), mapper_->GetNumMappedRanges()); // The range is shifted in real space but should still be the same in // quipper space. TestMappedRange( new_range, GetMappedAddressFromRanges(kMapRanges, arraysize(kMapRanges), range.addr)); } } // Test mapping of a giant map that overlaps with all existing ranges. TEST_F(AddressMapperTest, OverlapBig) { // A huge region that overlaps with all ranges in |kMapRanges|. const Range kBigRegion(0xa00, 0xff000000, 0x1234, 0); // Map all the ranges first. for (const Range& range : kMapRanges) ASSERT_TRUE(MapRange(range, false)); // Make sure overlap is detected before removing old ranges. ASSERT_FALSE(MapRange(kBigRegion, false)); ASSERT_TRUE(MapRange(kBigRegion, true)); EXPECT_EQ(1U, mapper_->GetNumMappedRanges()); TestMappedRange(kBigRegion, 0); // Given the list of previously unmapped addresses, test that the ones within // |kBigRegion| are now mapped; for the ones that are not, test that they are // not mapped. for (uint64_t addr : kAddressesNotInRanges) { uint64_t mapped_addr = UINT64_MAX; AddressMapper::MappingList::const_iterator addr_iter; bool map_success = mapper_->GetMappedAddressAndListIterator( addr, &mapped_addr, &addr_iter); if (kBigRegion.contains(addr)) { EXPECT_TRUE(map_success); EXPECT_EQ(addr - kBigRegion.addr, mapped_addr); } else { EXPECT_FALSE(map_success); } } // Check that addresses in the originally mapped ranges no longer map to the // same addresses if they fall within |kBigRegion|, and don't map at all if // they are not within |kBigRegion|. for (const Range& range : kMapRanges) { for (uint64_t addr = range.addr; addr < range.addr + range.size; addr += range.size / kNumRangeTestIntervals) { uint64_t mapped_addr = UINT64_MAX; AddressMapper::MappingList::const_iterator addr_iter; bool map_success = mapper_->GetMappedAddressAndListIterator( addr, &mapped_addr, &addr_iter); if (kBigRegion.contains(addr)) { EXPECT_TRUE(map_success); EXPECT_EQ(addr - kBigRegion.addr, mapped_addr); } else { EXPECT_FALSE(map_success); } } } EXPECT_EQ(kBigRegion.size, mapper_->GetMaxMappedLength()); } // Test a mapping at the end of memory space. TEST_F(AddressMapperTest, EndOfMemory) { // A region that extends to the end of the address space. const Range kEndRegion(0xffffffff00000000, 0x100000000, 0x3456, 0); ASSERT_TRUE(MapRange(kEndRegion, true)); EXPECT_EQ(1U, mapper_->GetNumMappedRanges()); TestMappedRange(kEndRegion, 0); } // Test mapping of an out-of-bounds mapping. TEST_F(AddressMapperTest, OutOfBounds) { // A region toward the end of address space that overruns the end of the // address space. const Range kOutOfBoundsRegion(0xffffffff00000000, 0x00000000, 0xccddeeff, 0); ASSERT_FALSE(MapRange(kOutOfBoundsRegion, false)); ASSERT_FALSE(MapRange(kOutOfBoundsRegion, true)); EXPECT_EQ(0, mapper_->GetNumMappedRanges()); uint64_t mapped_addr; AddressMapper::MappingList::const_iterator iter; EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator( kOutOfBoundsRegion.addr + 0x100, &mapped_addr, &iter)); } // Test mapping of a region that covers the entire memory space. Then map other // regions over it. TEST_F(AddressMapperTest, FullRange) { // A huge region that covers all of the available space. const Range kFullRegion(0, UINT64_MAX, 0xaabbccdd, 0); ASSERT_TRUE(MapRange(kFullRegion, false)); size_t num_expected_ranges = 1; EXPECT_EQ(num_expected_ranges, mapper_->GetNumMappedRanges()); TestMappedRange(kFullRegion, 0); // Map some smaller ranges. for (const Range& range : kMapRanges) { // Check for collision first. ASSERT_FALSE(MapRange(range, false)); ASSERT_TRUE(MapRange(range, true)); // Make sure the number of mapped ranges has increased by two. The mapping // should have split an existing range. num_expected_ranges += 2; EXPECT_EQ(num_expected_ranges, mapper_->GetNumMappedRanges()); } } // Test mapping of one range in the middle of an existing range. The existing // range should be split into two to accommodate it. Also tests the use of base // offsets. TEST_F(AddressMapperTest, SplitRangeWithOffsetBase) { // Define the two ranges, with distinct IDs. const Range kFirstRange(0x10000, 0x4000, 0x11223344, 0x5000); const Range kSecondRange(0x12000, 0x1000, 0x55667788, 0); // As a sanity check, make sure the second range is fully contained within the // first range. EXPECT_LT(kFirstRange.addr, kSecondRange.addr); EXPECT_GT(kFirstRange.addr + kFirstRange.size, kSecondRange.addr + kSecondRange.size); // Map the two ranges. ASSERT_TRUE(MapRange(kFirstRange, true)); ASSERT_TRUE(MapRange(kSecondRange, true)); // The first range should have been split into two parts to make way for the // second range. There should be a total of three ranges. EXPECT_EQ(3U, mapper_->GetNumMappedRanges()); // Now make sure the mappings are correct. // The first range has been split up. Define the expected ranges here. const Range kFirstRangeHead(0x10000, 0x2000, kFirstRange.id, 0x5000); const Range kFirstRangeTail(0x13000, 0x1000, kFirstRange.id, 0x8000); // Test the two remaining parts of the first range. TestMappedRange(kFirstRangeHead, 0); TestMappedRange(kFirstRangeTail, kFirstRangeTail.addr - kFirstRangeHead.addr); // Test the second range normally. TestMappedRange(kSecondRange, kSecondRange.addr - kFirstRange.addr); } // Test mappings that are not aligned to mmap page boundaries. TEST_F(AddressMapperTest, NotPageAligned) { mapper_->set_page_alignment(0x1000); // Some address ranges that do not begin on a page boundary. const Range kUnalignedRanges[] = { Range(0xff000100, 0x1fff00, 0xdeadbeef, 0x100), Range(0x00a00180, 0x10000, 0xcafebabe, 0x180), Range(0x0c000300, 0x1000800, 0x900df00d, 0x4300), Range(0x000017f0, 0x30000, 0x9000091e, 0x7f0), }; // Map the ranges. for (const Range& range : kUnalignedRanges) ASSERT_TRUE(MapRange(range, true)); EXPECT_EQ(4U, mapper_->GetNumMappedRanges()); // Now make sure the mappings are correct. // First region is mapped as usual, except it does not start at the page // boundary. TestMappedRange(kUnalignedRanges[0], 0x00000100); // Second region follows at the end of the first, but notice that its length // is a full number of pages, which means... TestMappedRange(kUnalignedRanges[1], 0x00200180); // ... the third region starts beyond the next page boundary: 0x211000 instead // of 0x210000. TestMappedRange(kUnalignedRanges[2], 0x00211300); // Similarly, the fourth region starts beyond the next page boundary: // 0x1212000 instead of 0x1211000. TestMappedRange(kUnalignedRanges[3], 0x012127f0); } // Have one mapping in the middle of another, with a nonzero page alignment // parameter. TEST_F(AddressMapperTest, SplitRangeWithPageAlignment) { mapper_->set_page_alignment(0x1000); // These should map just fine. const Range kRange0(0x3000, 0x8000, 0xdeadbeef, 0); const Range kRange1(0x5000, 0x2000, 0xfeedbabe, 0); EXPECT_TRUE(MapRange(kRange0, true)); EXPECT_TRUE(MapRange(kRange1, true)); EXPECT_EQ(3U, mapper_->GetNumMappedRanges()); // Determine the expected split mappings. const Range kRange0Head(0x3000, 0x2000, 0xdeadbeef, 0); const Range kRange0Tail(0x7000, 0x4000, 0xdeadbeef, 0x4000); // Everything should be mapped and split as usual. TestMappedRange(kRange0Head, 0); TestMappedRange(kRange0Tail, 0x4000); TestMappedRange(kRange1, 0x2000); } // Have one mapping in the middle of another, with a nonzero page alignment // parameter. The overlapping region will not be aligned to page boundaries. TEST_F(AddressMapperTest, MisalignedSplitRangeWithPageAlignment) { mapper_->set_page_alignment(0x1000); const Range kRange0(0x3000, 0x8000, 0xdeadbeef, 0); const Range kMisalignedRange(0x4800, 0x2000, 0xfeedbabe, 0); EXPECT_TRUE(MapRange(kRange0, true)); // The misaligned mapping should not find enough space to split the existing // range. It is not allowed to move the existing mapping. EXPECT_FALSE(MapRange(kMisalignedRange, true)); } } // namespace quipper