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