Reserve boot image memory in one go.

Load boot image components into the reserved memory.

Test: m test-art-host-gtest
Test: testrunner.py --host
Test: Pixel 2 XL boots.
Test: m test-art-target-gtest
Test: testrunner.py --target --optimizing
Bug: 77856493
Change-Id: I214f947979bc0bbfc6df4312527504e90b88a01d
diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc
index 396f12b..ab3d18f 100644
--- a/libartbase/base/mem_map_test.cc
+++ b/libartbase/base/mem_map_test.cc
@@ -540,6 +540,7 @@
                                     PROT_READ | PROT_WRITE,
                                     /* low_4gb */ false,
                                     /* reuse */ false,
+                                    /* reservation */ nullptr,
                                     &error_msg);
   ASSERT_TRUE(map.IsValid());
   ASSERT_TRUE(error_msg.empty());
@@ -549,6 +550,7 @@
                                      PROT_READ | PROT_WRITE,
                                      /* low_4gb */ false,
                                      /* reuse */ true,
+                                     /* reservation */ nullptr,
                                      &error_msg);
   ASSERT_TRUE(map2.IsValid());
   ASSERT_TRUE(error_msg.empty());
@@ -720,4 +722,108 @@
   }
 }
 
+TEST_F(MemMapTest, Reservation) {
+  CommonInit();
+  std::string error_msg;
+  ScratchFile scratch_file;
+  constexpr size_t kMapSize = 5 * kPageSize;
+  std::unique_ptr<uint8_t[]> data(new uint8_t[kMapSize]());
+  ASSERT_TRUE(scratch_file.GetFile()->WriteFully(&data[0], kMapSize));
+
+  MemMap reservation = MemMap::MapAnonymous("Test reservation",
+                                            /* addr */ nullptr,
+                                            kMapSize,
+                                            PROT_NONE,
+                                            /* low_4gb */ false,
+                                            &error_msg);
+  ASSERT_TRUE(reservation.IsValid());
+  ASSERT_TRUE(error_msg.empty());
+
+  // Map first part of the reservation.
+  constexpr size_t kChunk1Size = kPageSize - 1u;
+  static_assert(kChunk1Size < kMapSize, "We want to split the reservation.");
+  uint8_t* addr1 = reservation.Begin();
+  MemMap map1 = MemMap::MapFileAtAddress(addr1,
+                                         /* byte_count */ kChunk1Size,
+                                         PROT_READ,
+                                         MAP_PRIVATE,
+                                         scratch_file.GetFd(),
+                                         /* start */ 0,
+                                         /* low_4gb */ false,
+                                         scratch_file.GetFilename().c_str(),
+                                         /* reuse */ false,
+                                         &reservation,
+                                         &error_msg);
+  ASSERT_TRUE(map1.IsValid()) << error_msg;
+  ASSERT_TRUE(error_msg.empty());
+  ASSERT_EQ(map1.Size(), kChunk1Size);
+  ASSERT_EQ(addr1, map1.Begin());
+  ASSERT_TRUE(reservation.IsValid());
+  // Entire pages are taken from the `reservation`.
+  ASSERT_LT(map1.End(), map1.BaseEnd());
+  ASSERT_EQ(map1.BaseEnd(), reservation.Begin());
+
+  // Map second part as an anonymous mapping.
+  constexpr size_t kChunk2Size = 2 * kPageSize;
+  DCHECK_LT(kChunk2Size, reservation.Size());  // We want to split the reservation.
+  uint8_t* addr2 = reservation.Begin();
+  MemMap map2 = MemMap::MapAnonymous("MiddleReservation",
+                                     addr2,
+                                     /* byte_count */ kChunk2Size,
+                                     PROT_READ,
+                                     /* low_4gb */ false,
+                                     /* reuse */ false,
+                                     &reservation,
+                                     &error_msg);
+  ASSERT_TRUE(map2.IsValid()) << error_msg;
+  ASSERT_TRUE(error_msg.empty());
+  ASSERT_EQ(map2.Size(), kChunk2Size);
+  ASSERT_EQ(addr2, map2.Begin());
+  ASSERT_EQ(map2.End(), map2.BaseEnd());  // kChunk2Size is page aligned.
+  ASSERT_EQ(map2.BaseEnd(), reservation.Begin());
+
+  // Map the rest of the reservation except the last byte.
+  const size_t kChunk3Size = reservation.Size() - 1u;
+  uint8_t* addr3 = reservation.Begin();
+  MemMap map3 = MemMap::MapFileAtAddress(addr3,
+                                         /* byte_count */ kChunk3Size,
+                                         PROT_READ,
+                                         MAP_PRIVATE,
+                                         scratch_file.GetFd(),
+                                         /* start */ dchecked_integral_cast<size_t>(addr3 - addr1),
+                                         /* low_4gb */ false,
+                                         scratch_file.GetFilename().c_str(),
+                                         /* reuse */ false,
+                                         &reservation,
+                                         &error_msg);
+  ASSERT_TRUE(map3.IsValid()) << error_msg;
+  ASSERT_TRUE(error_msg.empty());
+  ASSERT_EQ(map3.Size(), kChunk3Size);
+  ASSERT_EQ(addr3, map3.Begin());
+  // Entire pages are taken from the `reservation`, so it's now exhausted.
+  ASSERT_FALSE(reservation.IsValid());
+
+  // Now split the MiddleReservation.
+  constexpr size_t kChunk2ASize = kPageSize - 1u;
+  DCHECK_LT(kChunk2ASize, map2.Size());  // We want to split the reservation.
+  MemMap map2a = map2.TakeReservedMemory(kChunk2ASize);
+  ASSERT_TRUE(map2a.IsValid()) << error_msg;
+  ASSERT_TRUE(error_msg.empty());
+  ASSERT_EQ(map2a.Size(), kChunk2ASize);
+  ASSERT_EQ(addr2, map2a.Begin());
+  ASSERT_TRUE(map2.IsValid());
+  ASSERT_LT(map2a.End(), map2a.BaseEnd());
+  ASSERT_EQ(map2a.BaseEnd(), map2.Begin());
+
+  // And take the rest of the middle reservation.
+  const size_t kChunk2BSize = map2.Size() - 1u;
+  uint8_t* addr2b = map2.Begin();
+  MemMap map2b = map2.TakeReservedMemory(kChunk2BSize);
+  ASSERT_TRUE(map2b.IsValid()) << error_msg;
+  ASSERT_TRUE(error_msg.empty());
+  ASSERT_EQ(map2b.Size(), kChunk2ASize);
+  ASSERT_EQ(addr2b, map2b.Begin());
+  ASSERT_FALSE(map2.IsValid());
+}
+
 }  // namespace art