blob: 34eabb04530051924fa47bb9947c19600b7cc280 [file] [log] [blame]
Yifan Hong537802d2018-08-15 13:15:42 -07001//
2// Copyright (C) 2018 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "update_engine/boot_control_android.h"
18
19#include <set>
20
21#include <android-base/strings.h>
David Andersond63cb3c2018-10-01 14:15:00 -070022#include <fs_mgr.h>
Yifan Hong537802d2018-08-15 13:15:42 -070023#include <gmock/gmock.h>
24#include <gtest/gtest.h>
25
26#include "update_engine/mock_boot_control_hal.h"
27#include "update_engine/mock_dynamic_partition_control.h"
28
29using android::base::Join;
30using android::fs_mgr::MetadataBuilder;
31using android::hardware::Void;
32using testing::_;
33using testing::AnyNumber;
34using testing::Contains;
35using testing::Eq;
36using testing::Invoke;
37using testing::Key;
38using testing::MakeMatcher;
39using testing::Matcher;
40using testing::MatcherInterface;
41using testing::MatchResultListener;
42using testing::NiceMock;
43using testing::Return;
44
45namespace chromeos_update_engine {
46
47constexpr const uint32_t kMaxNumSlots = 2;
48constexpr const char* kSlotSuffixes[kMaxNumSlots] = {"_a", "_b"};
49constexpr const char* kFakeDevicePath = "/fake/dev/path/";
50constexpr const char* kFakeMappedPath = "/fake/mapped/path/";
51constexpr const uint32_t kFakeMetadataSize = 65536;
Yifan Hong537802d2018-08-15 13:15:42 -070052
53// A map describing the size of each partition.
54using PartitionSizes = std::map<std::string, uint64_t>;
55
56// C++ standards do not allow uint64_t (aka unsigned long) to be the parameter
57// of user-defined literal operators.
58unsigned long long operator"" _MiB(unsigned long long x) { // NOLINT
59 return x << 20;
60}
61unsigned long long operator"" _GiB(unsigned long long x) { // NOLINT
62 return x << 30;
63}
64
65template <typename U, typename V>
66std::ostream& operator<<(std::ostream& os, const std::map<U, V>& param) {
67 os << "{";
68 bool first = true;
69 for (const auto& pair : param) {
70 if (!first)
71 os << ", ";
72 os << pair.first << ":" << pair.second;
73 first = false;
74 }
75 return os << "}";
76}
77
78inline std::string GetDevice(const std::string& name) {
79 return kFakeDevicePath + name;
80}
81inline std::string GetSuperDevice() {
David Andersond63cb3c2018-10-01 14:15:00 -070082 return GetDevice(fs_mgr_get_super_partition_name());
Yifan Hong537802d2018-08-15 13:15:42 -070083}
84
85struct TestParam {
86 uint32_t source;
87 uint32_t target;
88};
89std::ostream& operator<<(std::ostream& os, const TestParam& param) {
90 return os << "{source: " << param.source << ", target:" << param.target
91 << "}";
92}
93
94std::unique_ptr<MetadataBuilder> NewFakeMetadata(const PartitionSizes& sizes) {
95 auto builder = MetadataBuilder::New(10_GiB, kFakeMetadataSize, kMaxNumSlots);
96 EXPECT_NE(nullptr, builder);
97 if (builder == nullptr)
98 return nullptr;
99 for (const auto& pair : sizes) {
David Anderson4b92c1c2018-10-03 14:15:25 -0700100 auto p = builder->AddPartition(pair.first, 0 /* attr */);
Yifan Hong537802d2018-08-15 13:15:42 -0700101 EXPECT_TRUE(p && builder->ResizePartition(p, pair.second));
102 }
103 return builder;
104}
105
106class MetadataMatcher : public MatcherInterface<MetadataBuilder*> {
107 public:
108 explicit MetadataMatcher(const PartitionSizes& partition_sizes)
109 : partition_sizes_(partition_sizes) {}
110 bool MatchAndExplain(MetadataBuilder* metadata,
111 MatchResultListener* listener) const override {
112 bool success = true;
113 for (const auto& pair : partition_sizes_) {
114 auto p = metadata->FindPartition(pair.first);
115 if (p == nullptr) {
116 if (success)
117 *listener << "; ";
118 *listener << "No partition " << pair.first;
119 success = false;
120 continue;
121 }
122 if (p->size() != pair.second) {
123 if (success)
124 *listener << "; ";
125 *listener << "Partition " << pair.first << " has size " << p->size()
126 << ", expected " << pair.second;
127 success = false;
128 }
129 }
130 return success;
131 }
132
133 void DescribeTo(std::ostream* os) const override {
134 *os << "expect: " << partition_sizes_;
135 }
136
137 void DescribeNegationTo(std::ostream* os) const override {
138 *os << "expect not: " << partition_sizes_;
139 }
140
141 private:
142 PartitionSizes partition_sizes_;
143};
144
145inline Matcher<MetadataBuilder*> MetadataMatches(
146 const PartitionSizes& partition_sizes) {
147 return MakeMatcher(new MetadataMatcher(partition_sizes));
148}
149
150class BootControlAndroidTest : public ::testing::Test {
151 protected:
152 void SetUp() override {
153 // Fake init bootctl_
154 bootctl_.module_ = new NiceMock<MockBootControlHal>();
155 bootctl_.dynamic_control_ =
156 std::make_unique<NiceMock<MockDynamicPartitionControl>>();
157
158 ON_CALL(module(), getNumberSlots()).WillByDefault(Invoke([] {
159 return kMaxNumSlots;
160 }));
161 ON_CALL(module(), getSuffix(_, _))
162 .WillByDefault(Invoke([](auto slot, auto cb) {
163 EXPECT_LE(slot, kMaxNumSlots);
164 cb(slot < kMaxNumSlots ? kSlotSuffixes[slot] : "");
165 return Void();
166 }));
167
168 ON_CALL(dynamicControl(), IsDynamicPartitionsEnabled())
169 .WillByDefault(Return(true));
170 ON_CALL(dynamicControl(), GetDeviceDir(_))
171 .WillByDefault(Invoke([](auto path) {
172 *path = kFakeDevicePath;
173 return true;
174 }));
175 }
176
177 // Return the mocked HAL module.
178 NiceMock<MockBootControlHal>& module() {
179 return static_cast<NiceMock<MockBootControlHal>&>(*bootctl_.module_);
180 }
181
182 // Return the mocked DynamicPartitionControlInterface.
183 NiceMock<MockDynamicPartitionControl>& dynamicControl() {
184 return static_cast<NiceMock<MockDynamicPartitionControl>&>(
185 *bootctl_.dynamic_control_);
186 }
187
188 // Set the fake metadata to return when LoadMetadataBuilder is called on
189 // |slot|.
190 void SetMetadata(uint32_t slot, const PartitionSizes& sizes) {
191 EXPECT_CALL(dynamicControl(), LoadMetadataBuilder(GetSuperDevice(), slot))
192 .WillOnce(
193 Invoke([sizes](auto, auto) { return NewFakeMetadata(sizes); }));
194 }
195
196 // Expect that MapPartitionOnDeviceMapper is called on target() metadata slot
197 // with each partition in |partitions|.
198 void ExpectMap(const std::set<std::string>& partitions) {
199 // Error when MapPartitionOnDeviceMapper is called on unknown arguments.
200 ON_CALL(dynamicControl(), MapPartitionOnDeviceMapper(_, _, _, _))
201 .WillByDefault(Return(false));
202
203 for (const auto& partition : partitions) {
204 EXPECT_CALL(
205 dynamicControl(),
206 MapPartitionOnDeviceMapper(GetSuperDevice(), partition, target(), _))
207 .WillOnce(Invoke([this](auto, auto partition, auto, auto path) {
208 auto it = mapped_devices_.find(partition);
209 if (it != mapped_devices_.end()) {
210 *path = it->second;
211 return true;
212 }
213 mapped_devices_[partition] = *path = kFakeMappedPath + partition;
214 return true;
215 }));
216 }
217 }
218
219 // Expect that UnmapPartitionOnDeviceMapper is called on target() metadata
220 // slot with each partition in |partitions|.
221 void ExpectUnmap(const std::set<std::string>& partitions) {
222 // Error when UnmapPartitionOnDeviceMapper is called on unknown arguments.
223 ON_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(_, _))
224 .WillByDefault(Return(false));
225
226 for (const auto& partition : partitions) {
227 EXPECT_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(partition, _))
228 .WillOnce(Invoke([this](auto partition, auto) {
229 mapped_devices_.erase(partition);
230 return true;
231 }));
232 }
233 }
234
235 void ExpectRemap(const std::set<std::string>& partitions) {
236 ExpectUnmap(partitions);
237 ExpectMap(partitions);
238 }
239
240 void ExpectDevicesAreMapped(const std::set<std::string>& partitions) {
241 ASSERT_EQ(partitions.size(), mapped_devices_.size());
242 for (const auto& partition : partitions) {
243 EXPECT_THAT(mapped_devices_, Contains(Key(Eq(partition))))
244 << "Expect that " << partition << " is mapped, but it is not.";
245 }
246 }
247
Yifan Hong549eb362018-10-19 15:11:12 -0700248 void ExpectStoreMetadata(const PartitionSizes& partition_sizes) {
249 ExpectStoreMetadataMatch(MetadataMatches(partition_sizes));
250 }
251
252 virtual void ExpectStoreMetadataMatch(
253 const Matcher<MetadataBuilder*>& matcher) {
254 EXPECT_CALL(dynamicControl(),
255 StoreMetadata(GetSuperDevice(), matcher, target()))
256 .WillOnce(Return(true));
257 }
258
Yifan Hong537802d2018-08-15 13:15:42 -0700259 uint32_t source() { return slots_.source; }
260
261 uint32_t target() { return slots_.target; }
262
263 // Return partition names with suffix of source().
264 std::string S(const std::string& name) {
265 return name + std::string(kSlotSuffixes[source()]);
266 }
267
268 // Return partition names with suffix of target().
269 std::string T(const std::string& name) {
270 return name + std::string(kSlotSuffixes[target()]);
271 }
272
273 // Set source and target slots to use before testing.
274 void SetSlots(const TestParam& slots) {
275 slots_ = slots;
276
277 ON_CALL(module(), getCurrentSlot()).WillByDefault(Invoke([this] {
278 return source();
279 }));
280 // Should not store metadata to source slot.
281 EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, source()))
282 .Times(0);
283 }
284
285 BootControlAndroid bootctl_; // BootControlAndroid under test.
286 TestParam slots_;
287 // mapped devices through MapPartitionOnDeviceMapper.
288 std::map<std::string, std::string> mapped_devices_;
289};
290
291class BootControlAndroidTestP
292 : public BootControlAndroidTest,
293 public ::testing::WithParamInterface<TestParam> {
294 public:
295 void SetUp() override {
296 BootControlAndroidTest::SetUp();
297 SetSlots(GetParam());
298 }
299};
300
Yifan Hong537802d2018-08-15 13:15:42 -0700301// Test resize case. Grow if target metadata contains a partition with a size
302// less than expected.
303TEST_P(BootControlAndroidTestP, NeedGrowIfSizeNotMatchWhenResizing) {
304 PartitionSizes initial{{S("system"), 2_GiB},
305 {S("vendor"), 1_GiB},
306 {T("system"), 2_GiB},
307 {T("vendor"), 1_GiB}};
308 SetMetadata(source(), initial);
309 SetMetadata(target(), initial);
Yifan Hong549eb362018-10-19 15:11:12 -0700310 ExpectStoreMetadata({{S("system"), 2_GiB},
311 {S("vendor"), 1_GiB},
312 {T("system"), 3_GiB},
313 {T("vendor"), 1_GiB}});
Yifan Hong537802d2018-08-15 13:15:42 -0700314 ExpectRemap({T("system"), T("vendor")});
315
316 EXPECT_TRUE(bootctl_.InitPartitionMetadata(
317 target(), {{"system", 3_GiB}, {"vendor", 1_GiB}}));
318 ExpectDevicesAreMapped({T("system"), T("vendor")});
319}
320
321// Test resize case. Shrink if target metadata contains a partition with a size
322// greater than expected.
323TEST_P(BootControlAndroidTestP, NeedShrinkIfSizeNotMatchWhenResizing) {
324 PartitionSizes initial{{S("system"), 2_GiB},
325 {S("vendor"), 1_GiB},
326 {T("system"), 2_GiB},
327 {T("vendor"), 1_GiB}};
328 SetMetadata(source(), initial);
329 SetMetadata(target(), initial);
Yifan Hong549eb362018-10-19 15:11:12 -0700330 ExpectStoreMetadata({{S("system"), 2_GiB},
331 {S("vendor"), 1_GiB},
332 {T("system"), 2_GiB},
333 {T("vendor"), 150_MiB}});
Yifan Hong537802d2018-08-15 13:15:42 -0700334 ExpectRemap({T("system"), T("vendor")});
335
336 EXPECT_TRUE(bootctl_.InitPartitionMetadata(
337 target(), {{"system", 2_GiB}, {"vendor", 150_MiB}}));
338 ExpectDevicesAreMapped({T("system"), T("vendor")});
339}
340
341// Test adding partitions on the first run.
342TEST_P(BootControlAndroidTestP, AddPartitionToEmptyMetadata) {
343 SetMetadata(source(), {});
344 SetMetadata(target(), {});
Yifan Hong549eb362018-10-19 15:11:12 -0700345 ExpectStoreMetadata({{T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
Yifan Hong537802d2018-08-15 13:15:42 -0700346 ExpectRemap({T("system"), T("vendor")});
347
348 EXPECT_TRUE(bootctl_.InitPartitionMetadata(
349 target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}));
350 ExpectDevicesAreMapped({T("system"), T("vendor")});
351}
352
353// Test subsequent add case.
354TEST_P(BootControlAndroidTestP, AddAdditionalPartition) {
355 SetMetadata(source(), {{S("system"), 2_GiB}, {T("system"), 2_GiB}});
356 SetMetadata(target(), {{S("system"), 2_GiB}, {T("system"), 2_GiB}});
Yifan Hong549eb362018-10-19 15:11:12 -0700357 ExpectStoreMetadata(
358 {{S("system"), 2_GiB}, {T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
Yifan Hong537802d2018-08-15 13:15:42 -0700359 ExpectRemap({T("system"), T("vendor")});
360
361 EXPECT_TRUE(bootctl_.InitPartitionMetadata(
362 target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}));
363 ExpectDevicesAreMapped({T("system"), T("vendor")});
364}
365
366// Test delete one partition.
367TEST_P(BootControlAndroidTestP, DeletePartition) {
368 PartitionSizes initial{{S("system"), 2_GiB},
369 {S("vendor"), 1_GiB},
370 {T("system"), 2_GiB},
371 {T("vendor"), 1_GiB}};
372 SetMetadata(source(), initial);
373 SetMetadata(target(), initial);
Yifan Hong549eb362018-10-19 15:11:12 -0700374 ExpectStoreMetadata({{S("system"), 2_GiB},
375 {S("vendor"), 1_GiB},
376 {T("system"), 2_GiB},
377 {T("vendor"), 0}});
Yifan Hong537802d2018-08-15 13:15:42 -0700378 ExpectUnmap({T("system"), T("vendor")});
379 ExpectMap({T("system")});
380
381 EXPECT_TRUE(bootctl_.InitPartitionMetadata(
382 target(), {{"system", 2_GiB}, {"vendor", 0}}));
383 ExpectDevicesAreMapped({T("system")});
384}
385
386// Test delete all partitions.
387TEST_P(BootControlAndroidTestP, DeleteAll) {
388 PartitionSizes initial{{S("system"), 2_GiB},
389 {S("vendor"), 1_GiB},
390 {T("system"), 2_GiB},
391 {T("vendor"), 1_GiB}};
392 SetMetadata(source(), initial);
393 SetMetadata(target(), initial);
Yifan Hong549eb362018-10-19 15:11:12 -0700394 ExpectStoreMetadata({{S("system"), 2_GiB},
395 {S("vendor"), 1_GiB},
396 {T("system"), 0},
397 {T("vendor"), 0}});
Yifan Hong537802d2018-08-15 13:15:42 -0700398 ExpectUnmap({T("system"), T("vendor")});
399 ExpectMap({});
400
401 EXPECT_TRUE(
402 bootctl_.InitPartitionMetadata(target(), {{"system", 0}, {"vendor", 0}}));
403 ExpectDevicesAreMapped({});
404}
405
406// Test corrupt source metadata case. This shouldn't happen in practice,
407// because the device is already booted normally.
408TEST_P(BootControlAndroidTestP, CorruptedSourceMetadata) {
409 EXPECT_CALL(dynamicControl(), LoadMetadataBuilder(GetSuperDevice(), source()))
410 .WillOnce(Invoke([](auto, auto) { return nullptr; }));
411 EXPECT_FALSE(bootctl_.InitPartitionMetadata(target(), {}))
412 << "Should not be able to continue with corrupt source metadata";
413}
414
415// Test corrupt target metadata case. This may happen in practice.
416// BootControlAndroid should copy from source metadata and make necessary
417// modifications on it.
418TEST_P(BootControlAndroidTestP, CorruptedTargetMetadata) {
419 SetMetadata(source(),
420 {{S("system"), 2_GiB},
421 {S("vendor"), 1_GiB},
422 {T("system"), 0},
423 {T("vendor"), 0}});
424 EXPECT_CALL(dynamicControl(), LoadMetadataBuilder(GetSuperDevice(), target()))
425 .WillOnce(Invoke([](auto, auto) { return nullptr; }));
Yifan Hong549eb362018-10-19 15:11:12 -0700426 ExpectStoreMetadata({{S("system"), 2_GiB},
427 {S("vendor"), 1_GiB},
428 {T("system"), 3_GiB},
429 {T("vendor"), 150_MiB}});
Yifan Hong537802d2018-08-15 13:15:42 -0700430 ExpectRemap({T("system"), T("vendor")});
431 EXPECT_TRUE(bootctl_.InitPartitionMetadata(
432 target(), {{"system", 3_GiB}, {"vendor", 150_MiB}}));
433 ExpectDevicesAreMapped({T("system"), T("vendor")});
434}
435
436// Test that InitPartitionMetadata fail if there is not enough space on the
437// device.
438TEST_P(BootControlAndroidTestP, NotEnoughSpace) {
439 PartitionSizes initial{{S("system"), 3_GiB},
440 {S("vendor"), 2_GiB},
441 {T("system"), 0},
442 {T("vendor"), 0}};
443 SetMetadata(source(), initial);
444 SetMetadata(target(), initial);
445 EXPECT_FALSE(bootctl_.InitPartitionMetadata(
446 target(), {{"system", 3_GiB}, {"vendor", 3_GiB}}))
447 << "Should not be able to fit 11GiB data into 10GiB space";
448}
449
450INSTANTIATE_TEST_CASE_P(ParamTest,
451 BootControlAndroidTestP,
452 testing::Values(TestParam{0, 1}, TestParam{1, 0}));
453
454const PartitionSizes update_sizes_0() {
455 return {{"grown_a", 2_GiB},
456 {"shrunk_a", 1_GiB},
457 {"same_a", 100_MiB},
458 {"deleted_a", 150_MiB},
459 {"grown_b", 200_MiB},
460 {"shrunk_b", 0},
461 {"same_b", 0}};
462}
463
464const PartitionSizes update_sizes_1() {
465 return {
466 {"grown_a", 2_GiB},
467 {"shrunk_a", 1_GiB},
468 {"same_a", 100_MiB},
469 {"deleted_a", 150_MiB},
470 {"grown_b", 3_GiB},
471 {"shrunk_b", 150_MiB},
472 {"same_b", 100_MiB},
473 {"added_b", 150_MiB},
474 {"deleted_b", 0},
475 };
476}
477
478const PartitionSizes update_sizes_2() {
479 return {{"grown_a", 4_GiB},
480 {"shrunk_a", 100_MiB},
481 {"same_a", 100_MiB},
482 {"added_a", 0_MiB},
483 {"deleted_a", 64_MiB},
484 {"grown_b", 3_GiB},
485 {"shrunk_b", 150_MiB},
486 {"same_b", 100_MiB},
487 {"added_b", 150_MiB},
488 {"deleted_b", 0}};
489}
490
491// Test case for first update after the device is manufactured, in which
492// case the "other" slot is likely of size "0" (except system, which is
493// non-zero because of system_other partition)
494TEST_F(BootControlAndroidTest, SimulatedFirstUpdate) {
495 SetSlots({0, 1});
496
497 SetMetadata(source(), update_sizes_0());
498 SetMetadata(target(), update_sizes_0());
Yifan Hong549eb362018-10-19 15:11:12 -0700499 ExpectStoreMetadata(update_sizes_1());
Yifan Hong537802d2018-08-15 13:15:42 -0700500 ExpectUnmap({"grown_b", "shrunk_b", "same_b", "added_b", "deleted_b"});
501 ExpectMap({"grown_b", "shrunk_b", "same_b", "added_b"});
502
503 EXPECT_TRUE(bootctl_.InitPartitionMetadata(target(),
504 {{"grown", 3_GiB},
505 {"shrunk", 150_MiB},
506 {"same", 100_MiB},
507 {"added", 150_MiB},
508 {"deleted", 0_MiB}}));
509 ExpectDevicesAreMapped({"grown_b", "shrunk_b", "same_b", "added_b"});
510}
511
512// After first update, test for the second update. In the second update, the
513// "added" partition is deleted and "deleted" partition is re-added.
514TEST_F(BootControlAndroidTest, SimulatedSecondUpdate) {
515 SetSlots({1, 0});
516
517 SetMetadata(source(), update_sizes_1());
518 SetMetadata(target(), update_sizes_0());
519
Yifan Hong549eb362018-10-19 15:11:12 -0700520 ExpectStoreMetadata(update_sizes_2());
Yifan Hong537802d2018-08-15 13:15:42 -0700521 ExpectUnmap({"grown_a", "shrunk_a", "same_a", "added_a", "deleted_a"});
522 ExpectMap({"grown_a", "shrunk_a", "same_a", "deleted_a"});
523
524 EXPECT_TRUE(bootctl_.InitPartitionMetadata(target(),
525 {{"grown", 4_GiB},
526 {"shrunk", 100_MiB},
527 {"same", 100_MiB},
528 {"added", 0_MiB},
529 {"deleted", 64_MiB}}));
530 ExpectDevicesAreMapped({"grown_a", "shrunk_a", "same_a", "deleted_a"});
531}
532
533TEST_F(BootControlAndroidTest, ApplyingToCurrentSlot) {
534 SetSlots({1, 1});
535 EXPECT_FALSE(bootctl_.InitPartitionMetadata(target(), {}))
536 << "Should not be able to apply to current slot.";
537}
538
539} // namespace chromeos_update_engine