1*5225e6b1SAndroid Build Coastguard Worker // Copyright 2024, The Android Open Source Project 2*5225e6b1SAndroid Build Coastguard Worker // 3*5225e6b1SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); 4*5225e6b1SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License. 5*5225e6b1SAndroid Build Coastguard Worker // You may obtain a copy of the License at 6*5225e6b1SAndroid Build Coastguard Worker // 7*5225e6b1SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0 8*5225e6b1SAndroid Build Coastguard Worker // 9*5225e6b1SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software 10*5225e6b1SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, 11*5225e6b1SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*5225e6b1SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and 13*5225e6b1SAndroid Build Coastguard Worker // limitations under the License. 14*5225e6b1SAndroid Build Coastguard Worker 15*5225e6b1SAndroid Build Coastguard Worker //! This library provides APIs to work with data structures inside Android misc partition. 16*5225e6b1SAndroid Build Coastguard Worker //! 17*5225e6b1SAndroid Build Coastguard Worker //! Reference code: 18*5225e6b1SAndroid Build Coastguard Worker //! https://cs.android.com/android/platform/superproject/main/+/main:bootable/recovery/bootloader_message/include/bootloader_message/bootloader_message.h 19*5225e6b1SAndroid Build Coastguard Worker //! 20*5225e6b1SAndroid Build Coastguard Worker //! TODO(b/329716686): Generate rust bindings for misc API from recovery to reuse the up to date 21*5225e6b1SAndroid Build Coastguard Worker //! implementation 22*5225e6b1SAndroid Build Coastguard Worker 23*5225e6b1SAndroid Build Coastguard Worker #![cfg_attr(not(test), no_std)] 24*5225e6b1SAndroid Build Coastguard Worker 25*5225e6b1SAndroid Build Coastguard Worker use core::ffi::CStr; 26*5225e6b1SAndroid Build Coastguard Worker 27*5225e6b1SAndroid Build Coastguard Worker use zerocopy::{AsBytes, FromBytes, FromZeroes, Ref}; 28*5225e6b1SAndroid Build Coastguard Worker 29*5225e6b1SAndroid Build Coastguard Worker use liberror::{Error, Result}; 30*5225e6b1SAndroid Build Coastguard Worker 31*5225e6b1SAndroid Build Coastguard Worker /// Android boot modes type 32*5225e6b1SAndroid Build Coastguard Worker /// Usually obtained from BCB block of misc partition 33*5225e6b1SAndroid Build Coastguard Worker #[derive(PartialEq, Debug)] 34*5225e6b1SAndroid Build Coastguard Worker pub enum AndroidBootMode { 35*5225e6b1SAndroid Build Coastguard Worker /// Boot normally using A/B slots. 36*5225e6b1SAndroid Build Coastguard Worker Normal = 0, 37*5225e6b1SAndroid Build Coastguard Worker /// Boot into recovery mode using A/B slots. 38*5225e6b1SAndroid Build Coastguard Worker Recovery, 39*5225e6b1SAndroid Build Coastguard Worker /// Stop in bootloader fastboot mode. 40*5225e6b1SAndroid Build Coastguard Worker BootloaderBootOnce, 41*5225e6b1SAndroid Build Coastguard Worker } 42*5225e6b1SAndroid Build Coastguard Worker 43*5225e6b1SAndroid Build Coastguard Worker impl core::fmt::Display for AndroidBootMode { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result44*5225e6b1SAndroid Build Coastguard Worker fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 45*5225e6b1SAndroid Build Coastguard Worker match *self { 46*5225e6b1SAndroid Build Coastguard Worker AndroidBootMode::Normal => write!(f, "AndroidBootMode::Normal"), 47*5225e6b1SAndroid Build Coastguard Worker AndroidBootMode::Recovery => write!(f, "AndroidBootMode::Recovery"), 48*5225e6b1SAndroid Build Coastguard Worker AndroidBootMode::BootloaderBootOnce => write!(f, "AndroidBootMode::BootloaderBootOnce"), 49*5225e6b1SAndroid Build Coastguard Worker } 50*5225e6b1SAndroid Build Coastguard Worker } 51*5225e6b1SAndroid Build Coastguard Worker } 52*5225e6b1SAndroid Build Coastguard Worker 53*5225e6b1SAndroid Build Coastguard Worker /// Android bootloader message structure that usually placed in the first block of misc partition 54*5225e6b1SAndroid Build Coastguard Worker /// 55*5225e6b1SAndroid Build Coastguard Worker /// Reference code: 56*5225e6b1SAndroid Build Coastguard Worker /// https://cs.android.com/android/platform/superproject/main/+/95ec3cc1d879b92dd9db3bb4c4345c5fc812cdaa:bootable/recovery/bootloader_message/include/bootloader_message/bootloader_message.h;l=67 57*5225e6b1SAndroid Build Coastguard Worker #[repr(C, packed)] 58*5225e6b1SAndroid Build Coastguard Worker #[derive(AsBytes, FromBytes, FromZeroes, PartialEq, Copy, Clone, Debug)] 59*5225e6b1SAndroid Build Coastguard Worker pub struct BootloaderMessage { 60*5225e6b1SAndroid Build Coastguard Worker command: [u8; 32], 61*5225e6b1SAndroid Build Coastguard Worker status: [u8; 32], 62*5225e6b1SAndroid Build Coastguard Worker recovery: [u8; 768], 63*5225e6b1SAndroid Build Coastguard Worker stage: [u8; 32], 64*5225e6b1SAndroid Build Coastguard Worker reserved: [u8; 1184], 65*5225e6b1SAndroid Build Coastguard Worker } 66*5225e6b1SAndroid Build Coastguard Worker 67*5225e6b1SAndroid Build Coastguard Worker impl BootloaderMessage { 68*5225e6b1SAndroid Build Coastguard Worker /// BCB size in bytes 69*5225e6b1SAndroid Build Coastguard Worker pub const SIZE_BYTES: usize = 2048; 70*5225e6b1SAndroid Build Coastguard Worker 71*5225e6b1SAndroid Build Coastguard Worker /// Extract BootloaderMessage reference from bytes from_bytes_ref(buffer: &[u8]) -> Result<&BootloaderMessage>72*5225e6b1SAndroid Build Coastguard Worker pub fn from_bytes_ref(buffer: &[u8]) -> Result<&BootloaderMessage> { 73*5225e6b1SAndroid Build Coastguard Worker Ok(Ref::<_, BootloaderMessage>::new_from_prefix(buffer) 74*5225e6b1SAndroid Build Coastguard Worker .ok_or(Error::BufferTooSmall(Some(core::mem::size_of::<BootloaderMessage>())))? 75*5225e6b1SAndroid Build Coastguard Worker .0 76*5225e6b1SAndroid Build Coastguard Worker .into_ref()) 77*5225e6b1SAndroid Build Coastguard Worker } 78*5225e6b1SAndroid Build Coastguard Worker 79*5225e6b1SAndroid Build Coastguard Worker /// Extract AndroidBootMode from BCB command field boot_mode(&self) -> Result<AndroidBootMode>80*5225e6b1SAndroid Build Coastguard Worker pub fn boot_mode(&self) -> Result<AndroidBootMode> { 81*5225e6b1SAndroid Build Coastguard Worker let command = CStr::from_bytes_until_nul(&self.command) 82*5225e6b1SAndroid Build Coastguard Worker .map_err(|_| Error::Other(Some("Cannot read BCB command")))? 83*5225e6b1SAndroid Build Coastguard Worker .to_str() 84*5225e6b1SAndroid Build Coastguard Worker .map_err(|_| Error::InvalidInput)?; 85*5225e6b1SAndroid Build Coastguard Worker 86*5225e6b1SAndroid Build Coastguard Worker match command { 87*5225e6b1SAndroid Build Coastguard Worker "" => Ok(AndroidBootMode::Normal), 88*5225e6b1SAndroid Build Coastguard Worker "boot-recovery" | "boot-fastboot" => Ok(AndroidBootMode::Recovery), 89*5225e6b1SAndroid Build Coastguard Worker "bootonce-bootloader" => Ok(AndroidBootMode::BootloaderBootOnce), 90*5225e6b1SAndroid Build Coastguard Worker _ => Err(Error::Other(Some("Wrong BCB command"))), 91*5225e6b1SAndroid Build Coastguard Worker } 92*5225e6b1SAndroid Build Coastguard Worker } 93*5225e6b1SAndroid Build Coastguard Worker } 94*5225e6b1SAndroid Build Coastguard Worker 95*5225e6b1SAndroid Build Coastguard Worker #[cfg(test)] 96*5225e6b1SAndroid Build Coastguard Worker mod test { 97*5225e6b1SAndroid Build Coastguard Worker use crate::AndroidBootMode; 98*5225e6b1SAndroid Build Coastguard Worker use crate::BootloaderMessage; 99*5225e6b1SAndroid Build Coastguard Worker use zerocopy::AsBytes; 100*5225e6b1SAndroid Build Coastguard Worker 101*5225e6b1SAndroid Build Coastguard Worker impl Default for BootloaderMessage { default() -> Self102*5225e6b1SAndroid Build Coastguard Worker fn default() -> Self { 103*5225e6b1SAndroid Build Coastguard Worker BootloaderMessage { 104*5225e6b1SAndroid Build Coastguard Worker command: [0; 32], 105*5225e6b1SAndroid Build Coastguard Worker status: [0; 32], 106*5225e6b1SAndroid Build Coastguard Worker recovery: [0; 768], 107*5225e6b1SAndroid Build Coastguard Worker stage: [0; 32], 108*5225e6b1SAndroid Build Coastguard Worker reserved: [0; 1184], 109*5225e6b1SAndroid Build Coastguard Worker } 110*5225e6b1SAndroid Build Coastguard Worker } 111*5225e6b1SAndroid Build Coastguard Worker } 112*5225e6b1SAndroid Build Coastguard Worker 113*5225e6b1SAndroid Build Coastguard Worker #[test] test_bcb_empty_parsed_as_normal()114*5225e6b1SAndroid Build Coastguard Worker fn test_bcb_empty_parsed_as_normal() { 115*5225e6b1SAndroid Build Coastguard Worker let bcb = BootloaderMessage::default(); 116*5225e6b1SAndroid Build Coastguard Worker 117*5225e6b1SAndroid Build Coastguard Worker assert_eq!( 118*5225e6b1SAndroid Build Coastguard Worker BootloaderMessage::from_bytes_ref(bcb.as_bytes()).unwrap().boot_mode().unwrap(), 119*5225e6b1SAndroid Build Coastguard Worker AndroidBootMode::Normal 120*5225e6b1SAndroid Build Coastguard Worker ); 121*5225e6b1SAndroid Build Coastguard Worker } 122*5225e6b1SAndroid Build Coastguard Worker 123*5225e6b1SAndroid Build Coastguard Worker #[test] test_bcb_with_wrong_command_failed()124*5225e6b1SAndroid Build Coastguard Worker fn test_bcb_with_wrong_command_failed() { 125*5225e6b1SAndroid Build Coastguard Worker let command = "boot-wrong"; 126*5225e6b1SAndroid Build Coastguard Worker let mut bcb = BootloaderMessage::default(); 127*5225e6b1SAndroid Build Coastguard Worker bcb.command[..command.len()].copy_from_slice(command.as_bytes()); 128*5225e6b1SAndroid Build Coastguard Worker 129*5225e6b1SAndroid Build Coastguard Worker assert!(BootloaderMessage::from_bytes_ref(bcb.as_bytes()).unwrap().boot_mode().is_err()); 130*5225e6b1SAndroid Build Coastguard Worker } 131*5225e6b1SAndroid Build Coastguard Worker 132*5225e6b1SAndroid Build Coastguard Worker #[test] test_bcb_to_recovery_parsed()133*5225e6b1SAndroid Build Coastguard Worker fn test_bcb_to_recovery_parsed() { 134*5225e6b1SAndroid Build Coastguard Worker let command = "boot-recovery"; 135*5225e6b1SAndroid Build Coastguard Worker let mut bcb = BootloaderMessage::default(); 136*5225e6b1SAndroid Build Coastguard Worker bcb.command[..command.len()].copy_from_slice(command.as_bytes()); 137*5225e6b1SAndroid Build Coastguard Worker 138*5225e6b1SAndroid Build Coastguard Worker assert_eq!( 139*5225e6b1SAndroid Build Coastguard Worker BootloaderMessage::from_bytes_ref(bcb.as_bytes()).unwrap().boot_mode().unwrap(), 140*5225e6b1SAndroid Build Coastguard Worker AndroidBootMode::Recovery 141*5225e6b1SAndroid Build Coastguard Worker ); 142*5225e6b1SAndroid Build Coastguard Worker } 143*5225e6b1SAndroid Build Coastguard Worker 144*5225e6b1SAndroid Build Coastguard Worker #[test] test_bcb_to_fastboot_parsed_as_recovery()145*5225e6b1SAndroid Build Coastguard Worker fn test_bcb_to_fastboot_parsed_as_recovery() { 146*5225e6b1SAndroid Build Coastguard Worker let command = "boot-fastboot"; 147*5225e6b1SAndroid Build Coastguard Worker let mut bcb = BootloaderMessage::default(); 148*5225e6b1SAndroid Build Coastguard Worker bcb.command[..command.len()].copy_from_slice(command.as_bytes()); 149*5225e6b1SAndroid Build Coastguard Worker 150*5225e6b1SAndroid Build Coastguard Worker assert_eq!( 151*5225e6b1SAndroid Build Coastguard Worker BootloaderMessage::from_bytes_ref(bcb.as_bytes()).unwrap().boot_mode().unwrap(), 152*5225e6b1SAndroid Build Coastguard Worker AndroidBootMode::Recovery 153*5225e6b1SAndroid Build Coastguard Worker ); 154*5225e6b1SAndroid Build Coastguard Worker } 155*5225e6b1SAndroid Build Coastguard Worker 156*5225e6b1SAndroid Build Coastguard Worker #[test] test_bcb_to_bootloader_once_parsed()157*5225e6b1SAndroid Build Coastguard Worker fn test_bcb_to_bootloader_once_parsed() { 158*5225e6b1SAndroid Build Coastguard Worker let command = "bootonce-bootloader"; 159*5225e6b1SAndroid Build Coastguard Worker let mut bcb = BootloaderMessage::default(); 160*5225e6b1SAndroid Build Coastguard Worker bcb.command[..command.len()].copy_from_slice(command.as_bytes()); 161*5225e6b1SAndroid Build Coastguard Worker 162*5225e6b1SAndroid Build Coastguard Worker assert_eq!( 163*5225e6b1SAndroid Build Coastguard Worker BootloaderMessage::from_bytes_ref(bcb.as_bytes()).unwrap().boot_mode().unwrap(), 164*5225e6b1SAndroid Build Coastguard Worker AndroidBootMode::BootloaderBootOnce 165*5225e6b1SAndroid Build Coastguard Worker ); 166*5225e6b1SAndroid Build Coastguard Worker } 167*5225e6b1SAndroid Build Coastguard Worker } 168