1 // Copyright 2023, The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 extern crate bitflags; 16 extern crate crc32fast; 17 extern crate zerocopy; 18 19 use super::partition::{MetadataBytes, MetadataParseError, SlotBlock}; 20 use super::{ 21 BootTarget, BootToken, Bootability, Error, Manager, OneShot, RecoveryTarget, Slot, 22 SlotIterator, Suffix, UnbootableReason, 23 }; 24 use bitflags::bitflags; 25 use core::iter::zip; 26 use core::mem::size_of; 27 use crc32fast::Hasher; 28 use zerocopy::byteorder::big_endian::U32 as BigEndianU32; 29 use zerocopy::{AsBytes, ByteSlice, FromBytes, FromZeroes, Ref}; 30 31 const DEFAULT_PRIORITY: u8 = 15; 32 const DEFAULT_RETRIES: u8 = 7; 33 34 #[repr(C, packed)] 35 #[derive(Copy, Clone, Debug, PartialEq, Eq, AsBytes, FromBytes, FromZeroes)] 36 struct AbrSlotData { 37 priority: u8, 38 tries: u8, 39 successful: u8, 40 unbootable_reason: u8, 41 } 42 43 impl Default for AbrSlotData { default() -> Self44 fn default() -> Self { 45 Self { 46 priority: DEFAULT_PRIORITY, 47 tries: DEFAULT_RETRIES, 48 successful: 0, 49 unbootable_reason: 0, 50 } 51 } 52 } 53 54 #[repr(C, packed)] 55 #[derive(Copy, Clone, Debug, PartialEq, Eq, AsBytes, FromBytes, FromZeroes)] 56 struct OneShotFlags(u8); 57 58 bitflags! { 59 impl OneShotFlags: u8 { 60 /// No oneshot specified 61 const NONE = 0; 62 /// Oneshot boot to recovery mode 63 const RECOVERY = 1 << 0; 64 /// Oneshot boot to fastboot 65 const BOOTLOADER = 1 << 1; 66 } 67 } 68 69 impl From<OneShotFlags> for Option<OneShot> { from(flags: OneShotFlags) -> Self70 fn from(flags: OneShotFlags) -> Self { 71 match flags { 72 OneShotFlags::RECOVERY => Some(OneShot::Continue(RecoveryTarget::Dedicated)), 73 OneShotFlags::BOOTLOADER => Some(OneShot::Bootloader), 74 _ => None, 75 } 76 } 77 } 78 79 impl From<Option<OneShot>> for OneShotFlags { from(oneshot: Option<OneShot>) -> Self80 fn from(oneshot: Option<OneShot>) -> Self { 81 if let Some(target) = oneshot { 82 match target { 83 OneShot::Bootloader => Self::BOOTLOADER, 84 OneShot::Continue(RecoveryTarget::Dedicated) => Self::RECOVERY, 85 _ => Self::NONE, 86 } 87 } else { 88 Self::NONE 89 } 90 } 91 } 92 93 const ABR_MAGIC: &[u8; 4] = b"\0AB0"; 94 const ABR_VERSION_MAJOR: u8 = 2; 95 const ABR_VERSION_MINOR: u8 = 3; 96 97 #[repr(C, packed)] 98 #[derive(Copy, Clone, Debug, PartialEq, Eq, AsBytes, FromBytes, FromZeroes)] 99 struct AbrData { 100 magic: [u8; 4], 101 version_major: u8, 102 version_minor: u8, 103 reserved: [u8; 2], 104 slot_data: [AbrSlotData; 2], 105 oneshot_flag: OneShotFlags, 106 reserved2: [u8; 11], 107 crc32: BigEndianU32, 108 } 109 110 impl AbrData { calculate_crc32(&self) -> u32111 fn calculate_crc32(&self) -> u32 { 112 let mut hasher = Hasher::new(); 113 // Note: core::offset_of isn't stable yet, 114 // and size_of_val isn't permitted on unaligned structs. 115 hasher.update(&self.as_bytes()[..(size_of::<Self>() - size_of::<BigEndianU32>())]); 116 hasher.finalize() 117 } 118 } 119 120 impl MetadataBytes for AbrData { validate<B: ByteSlice>(buffer: B) -> Result<Ref<B, AbrData>, MetadataParseError>121 fn validate<B: ByteSlice>(buffer: B) -> Result<Ref<B, AbrData>, MetadataParseError> { 122 let abr_data = 123 Ref::<B, AbrData>::new_from_prefix(buffer).ok_or(MetadataParseError::BufferTooSmall)?.0; 124 125 if abr_data.magic != *ABR_MAGIC { 126 return Err(MetadataParseError::BadMagic); 127 } 128 if abr_data.version_major > ABR_VERSION_MAJOR { 129 return Err(MetadataParseError::BadVersion); 130 } 131 if abr_data.crc32.get() != abr_data.calculate_crc32() { 132 return Err(MetadataParseError::BadChecksum); 133 } 134 135 Ok(abr_data) 136 } 137 prepare_for_sync(&mut self)138 fn prepare_for_sync(&mut self) { 139 self.version_minor = ABR_VERSION_MINOR; 140 self.crc32 = self.calculate_crc32().into(); 141 } 142 } 143 144 impl Default for AbrData { default() -> Self145 fn default() -> Self { 146 let mut data = Self { 147 magic: *ABR_MAGIC, 148 version_major: ABR_VERSION_MAJOR, 149 version_minor: ABR_VERSION_MINOR, 150 reserved: Default::default(), 151 slot_data: Default::default(), 152 oneshot_flag: OneShotFlags::NONE, 153 reserved2: Default::default(), 154 crc32: BigEndianU32::ZERO, 155 }; 156 data.crc32.set(data.calculate_crc32()); 157 data 158 } 159 } 160 161 impl super::private::SlotGet for SlotBlock<'_, AbrData> { get_slot_by_number(&self, number: usize) -> Result<Slot, Error>162 fn get_slot_by_number(&self, number: usize) -> Result<Slot, Error> { 163 let lower_ascii_suffixes = ('a'..='z').map(Suffix); 164 let (suffix, &abr_slot) = zip(lower_ascii_suffixes, self.get_data().slot_data.iter()) 165 .nth(number) 166 .ok_or_else(|| Suffix::try_from(number).map_or(Error::Other, Error::NoSuchSlot))?; 167 168 let bootability = match (abr_slot.successful, abr_slot.tries) { 169 (s, _) if s != 0 => Bootability::Successful, 170 (0, t) if t > 0 => Bootability::Retriable(t.into()), 171 (_, _) => Bootability::Unbootable(abr_slot.unbootable_reason.into()), 172 }; 173 174 Ok(Slot { suffix, priority: abr_slot.priority.into(), bootability }) 175 } 176 } 177 178 impl Manager for SlotBlock<'_, AbrData> { get_boot_target(&self) -> BootTarget179 fn get_boot_target(&self) -> BootTarget { 180 self.slots_iter() 181 .filter(Slot::is_bootable) 182 .max_by_key(|slot| (slot.priority, slot.suffix.rank())) 183 .map_or(BootTarget::Recovery(RecoveryTarget::Dedicated), BootTarget::NormalBoot) 184 } 185 slots_iter(&self) -> SlotIterator186 fn slots_iter(&self) -> SlotIterator { 187 SlotIterator::new(self) 188 } 189 set_slot_unbootable( &mut self, slot_suffix: Suffix, reason: UnbootableReason, ) -> Result<(), Error>190 fn set_slot_unbootable( 191 &mut self, 192 slot_suffix: Suffix, 193 reason: UnbootableReason, 194 ) -> Result<(), Error> { 195 let (idx, slot) = self.get_index_and_slot_with_suffix(slot_suffix)?; 196 if slot.bootability == Bootability::Unbootable(reason) { 197 return Ok(()); 198 } 199 200 let abr_data = self.get_mut_data(); 201 let slot_data = &mut abr_data.slot_data[idx]; 202 slot_data.unbootable_reason = reason.into(); 203 slot_data.tries = 0; 204 slot_data.successful = 0; 205 206 Ok(()) 207 } 208 mark_boot_attempt(&mut self) -> Result<BootToken, Error>209 fn mark_boot_attempt(&mut self) -> Result<BootToken, Error> { 210 let target = if let Some(OneShot::Continue(r)) = self.get_oneshot_status() { 211 BootTarget::Recovery(r) 212 } else { 213 self.get_boot_target() 214 }; 215 let target_slot = match target { 216 BootTarget::NormalBoot(slot) => slot, 217 BootTarget::Recovery(RecoveryTarget::Slotted(_)) => Err(Error::OperationProhibited)?, 218 BootTarget::Recovery(RecoveryTarget::Dedicated) => { 219 // Even though boot to recovery does not cause a metadata update, 220 // we still need to gate access to the boot token. 221 return self.take_boot_token().ok_or(Error::OperationProhibited); 222 } 223 }; 224 225 let (idx, slot) = self.get_index_and_slot_with_suffix(target_slot.suffix)?; 226 227 match slot.bootability { 228 Bootability::Unbootable(_) => Err(Error::OperationProhibited), 229 Bootability::Retriable(_) => { 230 let abr_slot = &mut self.get_mut_data().slot_data[idx]; 231 abr_slot.tries -= 1; 232 if abr_slot.tries == 0 { 233 abr_slot.unbootable_reason = UnbootableReason::NoMoreTries.into(); 234 } 235 let token = self.take_boot_token().ok_or(Error::OperationProhibited)?; 236 Ok(token) 237 } 238 Bootability::Successful => { 239 let token = self.take_boot_token().ok_or(Error::OperationProhibited)?; 240 Ok(token) 241 } 242 } 243 } 244 set_active_slot(&mut self, slot_suffix: Suffix) -> Result<(), Error>245 fn set_active_slot(&mut self, slot_suffix: Suffix) -> Result<(), Error> { 246 let (idx, _) = self.get_index_and_slot_with_suffix(slot_suffix)?; 247 248 let abr_data = self.get_mut_data(); 249 for (i, slot) in abr_data.slot_data.iter_mut().enumerate() { 250 if i == idx { 251 *slot = Default::default(); 252 } else { 253 slot.priority = DEFAULT_PRIORITY - 1; 254 } 255 } 256 Ok(()) 257 } 258 get_oneshot_status(&self) -> Option<OneShot>259 fn get_oneshot_status(&self) -> Option<OneShot> { 260 self.get_data().oneshot_flag.into() 261 } 262 set_oneshot_status(&mut self, oneshot: OneShot) -> Result<(), Error>263 fn set_oneshot_status(&mut self, oneshot: OneShot) -> Result<(), Error> { 264 if Some(oneshot) == self.get_oneshot_status() { 265 return Ok(()); 266 } 267 268 let oneshot_flag = OneShotFlags::from(Some(oneshot)); 269 if oneshot_flag == OneShotFlags::NONE { 270 Err(match oneshot { 271 OneShot::Continue(RecoveryTarget::Slotted(_)) => Error::OperationProhibited, 272 _ => Error::Other, 273 }) 274 } else { 275 self.get_mut_data().oneshot_flag = oneshot_flag; 276 Ok(()) 277 } 278 } 279 clear_oneshot_status(&mut self)280 fn clear_oneshot_status(&mut self) { 281 if self.get_oneshot_status().is_some() { 282 self.get_mut_data().oneshot_flag = OneShotFlags::NONE; 283 } 284 } 285 write_back<B: gbl_storage::AsBlockDevice>(&mut self, block_dev: &mut B)286 fn write_back<B: gbl_storage::AsBlockDevice>(&mut self, block_dev: &mut B) { 287 self.sync_to_disk(block_dev); 288 } 289 } 290 291 impl<'a> SlotBlock<'a, AbrData> { get_index_and_slot_with_suffix(&self, slot_suffix: Suffix) -> Result<(usize, Slot), Error>292 fn get_index_and_slot_with_suffix(&self, slot_suffix: Suffix) -> Result<(usize, Slot), Error> { 293 self.slots_iter() 294 .enumerate() 295 .find(|(_, s)| s.suffix == slot_suffix) 296 .ok_or(Error::NoSuchSlot(slot_suffix)) 297 } 298 } 299 300 #[cfg(test)] 301 mod test { 302 use super::*; 303 use crate::slots::{partition::CacheStatus, Cursor}; 304 use gbl_storage::AsBlockDevice; 305 use gbl_storage_testlib::TestBlockDevice; 306 307 #[test] test_slot_block_defaults()308 fn test_slot_block_defaults() { 309 let sb: SlotBlock<AbrData> = Default::default(); 310 let expected: Vec<Slot> = vec![ 311 Slot { 312 suffix: 'a'.into(), 313 priority: DEFAULT_PRIORITY.into(), 314 bootability: Bootability::Retriable(sb.get_max_retries()), 315 }, 316 Slot { 317 suffix: 'b'.into(), 318 priority: DEFAULT_PRIORITY.into(), 319 bootability: Bootability::Retriable(sb.get_max_retries()), 320 }, 321 ]; 322 let actual: Vec<Slot> = sb.slots_iter().collect(); 323 assert_eq!(actual, expected); 324 assert_eq!(sb.get_oneshot_status(), None); 325 } 326 327 #[test] test_suffix()328 fn test_suffix() { 329 let slot = Slot { suffix: 'a'.into(), ..Default::default() }; 330 assert_eq!(BootTarget::Recovery(RecoveryTarget::Dedicated).suffix(), 'r'.into()); 331 assert_eq!(BootTarget::Recovery(RecoveryTarget::Slotted(slot)).suffix(), slot.suffix); 332 assert_eq!(BootTarget::NormalBoot(slot).suffix(), slot.suffix); 333 } 334 335 #[test] test_slot_block_parse()336 fn test_slot_block_parse() { 337 let abr: AbrData = Default::default(); 338 assert_eq!(AbrData::validate(abr.as_bytes()), Ok(Ref::new(abr.as_bytes()).unwrap())); 339 } 340 341 #[test] test_slot_block_parse_buffer_too_small()342 fn test_slot_block_parse_buffer_too_small() { 343 let buffer: [u8; 0] = Default::default(); 344 assert_eq!(AbrData::validate(&buffer[..]), Err(MetadataParseError::BufferTooSmall),); 345 } 346 347 #[test] test_slot_block_parse_bad_magic()348 fn test_slot_block_parse_bad_magic() { 349 let mut abr: AbrData = Default::default(); 350 abr.magic[0] += 1; 351 assert_eq!(AbrData::validate(abr.as_bytes()), Err(MetadataParseError::BadMagic)); 352 } 353 354 #[test] test_slot_block_parse_bad_version_major()355 fn test_slot_block_parse_bad_version_major() { 356 let mut abr: AbrData = Default::default(); 357 abr.version_major = 15; 358 assert_eq!(AbrData::validate(abr.as_bytes()), Err(MetadataParseError::BadVersion)); 359 } 360 361 #[test] test_slot_block_parse_bad_crc()362 fn test_slot_block_parse_bad_crc() { 363 let mut abr: AbrData = Default::default(); 364 let bad_crc = abr.crc32.get() ^ BigEndianU32::MAX_VALUE.get(); 365 abr.crc32 = bad_crc.into(); 366 assert_eq!(AbrData::validate(abr.as_bytes()), Err(MetadataParseError::BadChecksum)); 367 } 368 369 #[test] test_slot_mark_boot_attempt()370 fn test_slot_mark_boot_attempt() { 371 let mut sb: SlotBlock<AbrData> = Default::default(); 372 assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(()))); 373 assert_eq!( 374 sb.slots_iter().next().unwrap(), 375 Slot { 376 suffix: 'a'.into(), 377 priority: DEFAULT_PRIORITY.into(), 378 bootability: Bootability::Retriable((DEFAULT_RETRIES - 1).into()) 379 } 380 ); 381 382 // Make sure we can call exactly once 383 assert_eq!(sb.mark_boot_attempt(), Err(Error::OperationProhibited)); 384 } 385 386 #[test] test_slot_mark_boot_attempt_tracks_active()387 fn test_slot_mark_boot_attempt_tracks_active() { 388 let mut sb: SlotBlock<AbrData> = Default::default(); 389 assert!(sb.set_active_slot('b'.into()).is_ok()); 390 391 assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(()))); 392 assert_eq!( 393 sb.get_boot_target(), 394 BootTarget::NormalBoot(Slot { 395 suffix: 'b'.into(), 396 priority: DEFAULT_PRIORITY.into(), 397 bootability: Bootability::Retriable((DEFAULT_RETRIES - 1).into()) 398 }) 399 ); 400 } 401 402 #[test] test_slot_mark_boot_attempt_no_more_tries()403 fn test_slot_mark_boot_attempt_no_more_tries() { 404 let mut sb: SlotBlock<AbrData> = Default::default(); 405 sb.get_mut_data().slot_data[0].tries = 1; 406 assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(()))); 407 assert_eq!( 408 sb.slots_iter().next().unwrap(), 409 Slot { 410 suffix: 'a'.into(), 411 priority: DEFAULT_PRIORITY.into(), 412 bootability: Bootability::Unbootable(UnbootableReason::NoMoreTries) 413 } 414 ); 415 } 416 417 #[test] test_slot_mark_boot_attempt_successful()418 fn test_slot_mark_boot_attempt_successful() { 419 let mut sb: SlotBlock<AbrData> = Default::default(); 420 sb.get_mut_data().slot_data[0].successful = 1; 421 let target = BootTarget::NormalBoot(Slot { 422 suffix: 'a'.into(), 423 priority: DEFAULT_PRIORITY.into(), 424 bootability: Bootability::Successful, 425 }); 426 assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(()))); 427 assert_eq!(sb.get_boot_target(), target); 428 } 429 430 #[test] test_slot_mark_tried_recovery()431 fn test_slot_mark_tried_recovery() { 432 let mut sb: SlotBlock<AbrData> = Default::default(); 433 let recovery_tgt = BootTarget::Recovery(RecoveryTarget::Dedicated); 434 assert!(sb.set_slot_unbootable('a'.into(), UnbootableReason::UserRequested).is_ok()); 435 assert!(sb.set_slot_unbootable('b'.into(), UnbootableReason::UserRequested).is_ok()); 436 assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(()))); 437 438 // Make sure a second attempt fails due to the moved boot token 439 assert_eq!(sb.mark_boot_attempt(), Err(Error::OperationProhibited)); 440 } 441 442 #[test] test_slot_mark_tried_recovery_oneshot()443 fn test_slot_mark_tried_recovery_oneshot() { 444 let mut sb: SlotBlock<AbrData> = Default::default(); 445 let tgt = sb.get_boot_target(); 446 assert!(sb.set_oneshot_status(OneShot::Continue(RecoveryTarget::Dedicated)).is_ok()); 447 assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(()))); 448 449 // Verify that tries weren't decremented 450 assert_eq!(sb.get_boot_target(), tgt); 451 } 452 453 macro_rules! set_unbootable_tests { 454 ($($name:ident: $value:expr,)*) => { 455 $( 456 #[test] 457 fn $name() { 458 let mut sb: SlotBlock<AbrData> = Default::default(); 459 let suffix: Suffix = 'a'.into(); 460 assert_eq!(sb.set_slot_unbootable(suffix, $value), Ok(())); 461 assert_eq!(sb.slots_iter() 462 .find(|s| s.suffix == suffix) 463 .unwrap() 464 .bootability, 465 Bootability::Unbootable($value) 466 ); 467 } 468 )* 469 } 470 } 471 472 use UnbootableReason::*; 473 set_unbootable_tests! { 474 test_set_unbootable_no_more_tries: NoMoreTries, 475 test_set_unbootable_system_update: SystemUpdate, 476 test_set_unbootable_user_requested: UserRequested, 477 test_set_unbootable_verification_failure: VerificationFailure, 478 test_set_unbootable_unknown: Unknown, 479 } 480 481 #[test] test_no_bootable_slots_boot_recovery()482 fn test_no_bootable_slots_boot_recovery() { 483 let mut sb: SlotBlock<AbrData> = Default::default(); 484 let v: Vec<Slot> = sb.slots_iter().collect(); 485 for slot in v { 486 assert_eq!( 487 sb.set_slot_unbootable(slot.suffix, UnbootableReason::UserRequested), 488 Ok(()) 489 ); 490 } 491 assert_eq!(sb.get_boot_target(), BootTarget::Recovery(RecoveryTarget::Dedicated)); 492 } 493 494 #[test] test_set_active_slot()495 fn test_set_active_slot() { 496 let mut sb: SlotBlock<AbrData> = Default::default(); 497 let v: Vec<Slot> = sb.slots_iter().collect(); 498 499 assert_eq!(sb.get_boot_target(), BootTarget::NormalBoot(v[0])); 500 for slot in v.iter() { 501 assert_eq!(sb.set_active_slot(slot.suffix), Ok(())); 502 assert_eq!(sb.get_boot_target(), BootTarget::NormalBoot(*slot)); 503 } 504 } 505 506 #[test] test_set_active_slot_no_such_slot()507 fn test_set_active_slot_no_such_slot() { 508 let mut sb: SlotBlock<AbrData> = Default::default(); 509 let bad_suffix: Suffix = '$'.into(); 510 assert_eq!(sb.set_active_slot(bad_suffix), Err(Error::NoSuchSlot(bad_suffix))); 511 } 512 513 #[test] test_get_slot_last_set_active()514 fn test_get_slot_last_set_active() { 515 let mut sb: SlotBlock<AbrData> = Default::default(); 516 let v: Vec<Slot> = sb.slots_iter().collect(); 517 assert_eq!(sb.set_active_slot(v[0].suffix), Ok(())); 518 assert_eq!(sb.get_slot_last_set_active(), v[0]); 519 for slot in v.iter() { 520 assert_eq!(sb.set_slot_unbootable(slot.suffix, NoMoreTries), Ok(())); 521 } 522 523 assert_eq!(sb.get_slot_last_set_active(), sb.slots_iter().next().unwrap()); 524 } 525 526 macro_rules! set_oneshot_tests { 527 ($($name:ident: $value:expr,)*) => { 528 $( 529 #[test] 530 fn $name(){ 531 let mut sb: SlotBlock<AbrData> = Default::default(); 532 assert_eq!(sb.set_oneshot_status($value), Ok(())); 533 assert_eq!(sb.get_oneshot_status(), Some($value)); 534 535 assert_eq!(sb.get_boot_target(), 536 BootTarget::NormalBoot( 537 Slot{ 538 suffix: 'a'.into(), 539 priority: DEFAULT_PRIORITY.into(), 540 bootability: Bootability::Retriable(sb.get_max_retries()), 541 }, 542 )); 543 } 544 )* 545 } 546 } 547 548 set_oneshot_tests! { 549 test_set_oneshot_bootloader: OneShot::Bootloader, 550 test_set_oneshot_recovery: OneShot::Continue(RecoveryTarget::Dedicated), 551 } 552 553 #[test] test_clear_oneshot_status()554 fn test_clear_oneshot_status() { 555 let mut sb: SlotBlock<AbrData> = Default::default(); 556 assert_eq!(sb.set_oneshot_status(OneShot::Bootloader), Ok(())); 557 sb.clear_oneshot_status(); 558 assert_eq!(sb.get_oneshot_status(), None); 559 } 560 561 #[test] test_set_oneshot_mistaken_recovery_slotted()562 fn test_set_oneshot_mistaken_recovery_slotted() { 563 let mut sb: SlotBlock<AbrData> = Default::default(); 564 let slot = sb.slots_iter().next().unwrap(); 565 assert_eq!( 566 sb.set_oneshot_status(OneShot::Continue(RecoveryTarget::Slotted(slot))), 567 Err(Error::OperationProhibited) 568 ); 569 } 570 571 #[test] test_deserialize_default_to_dirty_cache()572 fn test_deserialize_default_to_dirty_cache() { 573 let mut abr_data: AbrData = Default::default(); 574 // Changing the success both invalidates the crc 575 // and lets us verify that the deserialized slot block 576 // uses defaulted backing bytes instead of the provided bytes. 577 abr_data.slot_data[0].successful = 1; 578 let sb = SlotBlock::<AbrData>::deserialize( 579 abr_data.as_bytes(), 580 "partition_moniker", 581 0, 582 BootToken(()), 583 ); 584 assert_eq!(sb.cache_status(), CacheStatus::Dirty); 585 assert_eq!( 586 sb.slots_iter().next().unwrap().bootability, 587 Bootability::Retriable(DEFAULT_RETRIES.into()) 588 ); 589 } 590 591 #[test] test_deserialize_modified_to_clean_cache()592 fn test_deserialize_modified_to_clean_cache() { 593 let mut abr_data: AbrData = Default::default(); 594 abr_data.slot_data[0].successful = 1; 595 // If we recalculate the crc, 596 // that just means we have a metadata block that stores 597 // relevant, non-default information. 598 abr_data.crc32.set(abr_data.calculate_crc32()); 599 let sb = SlotBlock::<AbrData>::deserialize( 600 abr_data.as_bytes(), 601 "partition_moniker", 602 0, 603 BootToken(()), 604 ); 605 assert_eq!(sb.cache_status(), CacheStatus::Clean); 606 assert_eq!(sb.slots_iter().next().unwrap().bootability, Bootability::Successful); 607 } 608 609 #[test] test_writeback()610 fn test_writeback() { 611 const PARTITION: &str = "test_partition"; 612 const OFFSET: u64 = 2112; // Deliberately wrong to test propagation of parameter. 613 let mut block_dev: TestBlockDevice = 614 include_bytes!("../../testdata/writeback_test_disk.bin").as_slice().into(); 615 assert!(block_dev.sync_gpt().is_ok()); 616 let mut sb: SlotBlock<AbrData> = Default::default(); 617 sb.partition = PARTITION; 618 sb.partition_offset = OFFSET; 619 620 let mut read_buffer: [u8; size_of::<AbrData>()] = Default::default(); 621 622 // Clean cache, write_back is a no-op 623 sb.write_back(&mut block_dev); 624 let res = block_dev.read_gpt_partition(PARTITION, OFFSET, &mut read_buffer); 625 assert!(res.is_ok()); 626 assert_eq!(read_buffer, [0; std::mem::size_of::<AbrData>()]); 627 628 // Make a change, write_back writes back to the defined partition 629 // at the defined offset. 630 assert_eq!(sb.set_oneshot_status(OneShot::Bootloader), Ok(())); 631 assert_eq!(sb.cache_status(), CacheStatus::Dirty); 632 633 sb.write_back(&mut block_dev); 634 let res = block_dev.read_gpt_partition(PARTITION, OFFSET, &mut read_buffer); 635 assert!(res.is_ok()); 636 assert_eq!(read_buffer, sb.get_data().as_bytes()); 637 assert_eq!(sb.cache_status(), CacheStatus::Clean); 638 } 639 640 #[test] test_writeback_with_cursor()641 fn test_writeback_with_cursor() { 642 const PARTITION: &str = "test_partition"; 643 const OFFSET: u64 = 2112; // Deliberately wrong to test propagation of parameter. 644 let mut block_dev: TestBlockDevice = 645 include_bytes!("../../testdata/writeback_test_disk.bin").as_slice().into(); 646 assert!(block_dev.sync_gpt().is_ok()); 647 let mut read_buffer: [u8; size_of::<AbrData>()] = Default::default(); 648 let mut abr_data; 649 650 let mut sb: SlotBlock<AbrData> = Default::default(); 651 sb.partition = PARTITION; 652 sb.partition_offset = OFFSET; 653 654 // New block to trigger drop on the cursor. 655 { 656 let mut cursor = Cursor { ctx: sb, block_dev: &mut block_dev }; 657 assert!(cursor.ctx.set_active_slot('b'.into()).is_ok()); 658 abr_data = cursor.ctx.get_data().clone(); 659 } 660 661 // Need to manually recalculate crc because the cursor updates that 662 // right before writing to disk. 663 abr_data.prepare_for_sync(); 664 let res = block_dev.read_gpt_partition(PARTITION, OFFSET, &mut read_buffer); 665 assert!(res.is_ok()); 666 assert_eq!(read_buffer, abr_data.as_bytes()); 667 } 668 } 669