xref: /aosp_15_r20/bootable/libbootloader/gbl/libmisc/src/lib.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
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