xref: /aosp_15_r20/bootable/libbootloader/gbl/libmisc/src/lib.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
1 // Copyright 2024, 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 //! This library provides APIs to work with data structures inside Android misc partition.
16 //!
17 //! Reference code:
18 //! https://cs.android.com/android/platform/superproject/main/+/main:bootable/recovery/bootloader_message/include/bootloader_message/bootloader_message.h
19 //!
20 //! TODO(b/329716686): Generate rust bindings for misc API from recovery to reuse the up to date
21 //! implementation
22 
23 #![cfg_attr(not(test), no_std)]
24 
25 use core::ffi::CStr;
26 
27 use zerocopy::{AsBytes, FromBytes, FromZeroes, Ref};
28 
29 use liberror::{Error, Result};
30 
31 /// Android boot modes type
32 /// Usually obtained from BCB block of misc partition
33 #[derive(PartialEq, Debug)]
34 pub enum AndroidBootMode {
35     /// Boot normally using A/B slots.
36     Normal = 0,
37     /// Boot into recovery mode using A/B slots.
38     Recovery,
39     /// Stop in bootloader fastboot mode.
40     BootloaderBootOnce,
41 }
42 
43 impl core::fmt::Display for AndroidBootMode {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result44     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45         match *self {
46             AndroidBootMode::Normal => write!(f, "AndroidBootMode::Normal"),
47             AndroidBootMode::Recovery => write!(f, "AndroidBootMode::Recovery"),
48             AndroidBootMode::BootloaderBootOnce => write!(f, "AndroidBootMode::BootloaderBootOnce"),
49         }
50     }
51 }
52 
53 /// Android bootloader message structure that usually placed in the first block of misc partition
54 ///
55 /// Reference code:
56 /// https://cs.android.com/android/platform/superproject/main/+/95ec3cc1d879b92dd9db3bb4c4345c5fc812cdaa:bootable/recovery/bootloader_message/include/bootloader_message/bootloader_message.h;l=67
57 #[repr(C, packed)]
58 #[derive(AsBytes, FromBytes, FromZeroes, PartialEq, Copy, Clone, Debug)]
59 pub struct BootloaderMessage {
60     command: [u8; 32],
61     status: [u8; 32],
62     recovery: [u8; 768],
63     stage: [u8; 32],
64     reserved: [u8; 1184],
65 }
66 
67 impl BootloaderMessage {
68     /// BCB size in bytes
69     pub const SIZE_BYTES: usize = 2048;
70 
71     /// Extract BootloaderMessage reference from bytes
from_bytes_ref(buffer: &[u8]) -> Result<&BootloaderMessage>72     pub fn from_bytes_ref(buffer: &[u8]) -> Result<&BootloaderMessage> {
73         Ok(Ref::<_, BootloaderMessage>::new_from_prefix(buffer)
74             .ok_or(Error::BufferTooSmall(Some(core::mem::size_of::<BootloaderMessage>())))?
75             .0
76             .into_ref())
77     }
78 
79     /// Extract AndroidBootMode from BCB command field
boot_mode(&self) -> Result<AndroidBootMode>80     pub fn boot_mode(&self) -> Result<AndroidBootMode> {
81         let command = CStr::from_bytes_until_nul(&self.command)
82             .map_err(|_| Error::Other(Some("Cannot read BCB command")))?
83             .to_str()
84             .map_err(|_| Error::InvalidInput)?;
85 
86         match command {
87             "" => Ok(AndroidBootMode::Normal),
88             "boot-recovery" | "boot-fastboot" => Ok(AndroidBootMode::Recovery),
89             "bootonce-bootloader" => Ok(AndroidBootMode::BootloaderBootOnce),
90             _ => Err(Error::Other(Some("Wrong BCB command"))),
91         }
92     }
93 }
94 
95 #[cfg(test)]
96 mod test {
97     use crate::AndroidBootMode;
98     use crate::BootloaderMessage;
99     use zerocopy::AsBytes;
100 
101     impl Default for BootloaderMessage {
default() -> Self102         fn default() -> Self {
103             BootloaderMessage {
104                 command: [0; 32],
105                 status: [0; 32],
106                 recovery: [0; 768],
107                 stage: [0; 32],
108                 reserved: [0; 1184],
109             }
110         }
111     }
112 
113     #[test]
test_bcb_empty_parsed_as_normal()114     fn test_bcb_empty_parsed_as_normal() {
115         let bcb = BootloaderMessage::default();
116 
117         assert_eq!(
118             BootloaderMessage::from_bytes_ref(bcb.as_bytes()).unwrap().boot_mode().unwrap(),
119             AndroidBootMode::Normal
120         );
121     }
122 
123     #[test]
test_bcb_with_wrong_command_failed()124     fn test_bcb_with_wrong_command_failed() {
125         let command = "boot-wrong";
126         let mut bcb = BootloaderMessage::default();
127         bcb.command[..command.len()].copy_from_slice(command.as_bytes());
128 
129         assert!(BootloaderMessage::from_bytes_ref(bcb.as_bytes()).unwrap().boot_mode().is_err());
130     }
131 
132     #[test]
test_bcb_to_recovery_parsed()133     fn test_bcb_to_recovery_parsed() {
134         let command = "boot-recovery";
135         let mut bcb = BootloaderMessage::default();
136         bcb.command[..command.len()].copy_from_slice(command.as_bytes());
137 
138         assert_eq!(
139             BootloaderMessage::from_bytes_ref(bcb.as_bytes()).unwrap().boot_mode().unwrap(),
140             AndroidBootMode::Recovery
141         );
142     }
143 
144     #[test]
test_bcb_to_fastboot_parsed_as_recovery()145     fn test_bcb_to_fastboot_parsed_as_recovery() {
146         let command = "boot-fastboot";
147         let mut bcb = BootloaderMessage::default();
148         bcb.command[..command.len()].copy_from_slice(command.as_bytes());
149 
150         assert_eq!(
151             BootloaderMessage::from_bytes_ref(bcb.as_bytes()).unwrap().boot_mode().unwrap(),
152             AndroidBootMode::Recovery
153         );
154     }
155 
156     #[test]
test_bcb_to_bootloader_once_parsed()157     fn test_bcb_to_bootloader_once_parsed() {
158         let command = "bootonce-bootloader";
159         let mut bcb = BootloaderMessage::default();
160         bcb.command[..command.len()].copy_from_slice(command.as_bytes());
161 
162         assert_eq!(
163             BootloaderMessage::from_bytes_ref(bcb.as_bytes()).unwrap().boot_mode().unwrap(),
164             AndroidBootMode::BootloaderBootOnce
165         );
166     }
167 }
168