starnix_core/device/android/
bootloader_message_store.rs1use crate::device::remote_block_device::RemoteBlockDevice;
6use crate::task::CurrentTask;
7use anyhow::{Error, anyhow};
8use bstr::{BStr, ByteSlice as _};
9use starnix_logging::log_info;
10use starnix_sync::{Locked, Unlocked};
11use static_assertions::const_assert_eq;
12use std::sync::{Arc, Weak};
13use zerocopy::{FromBytes, Immutable, KnownLayout};
14
15#[derive(Debug)]
23pub struct AndroidBootloaderMessageStore(Weak<RemoteBlockDevice>);
24
25#[repr(C)]
27#[derive(Copy, Clone, Immutable, KnownLayout, FromBytes)]
28struct BootloaderMessageRaw {
29 command: [u8; 32],
30 _status: [u8; 32],
31 recovery: [u8; 768],
32 _stage: [u8; 32],
33 _reserved: [u8; 1184],
34}
35
36fn read_null_terminated(buf: &[u8]) -> &BStr {
37 if let Some((prefix, _)) = buf.split_once_str(&[0u8]) {
38 prefix.as_bstr()
39 } else {
40 buf.as_bstr()
41 }
42}
43
44#[derive(Debug, Default, Eq, PartialEq)]
45pub enum BootloaderMessage {
46 BootRecovery(Vec<String>),
48 #[default]
49 Unknown,
50}
51
52impl TryFrom<BootloaderMessageRaw> for BootloaderMessage {
53 type Error = anyhow::Error;
54
55 fn try_from(value: BootloaderMessageRaw) -> Result<Self, Self::Error> {
56 let command = read_null_terminated(&value.command).to_str()?;
57 if command.is_empty() {
58 return Err(anyhow!("No command written to bootloader messages"));
59 }
60 match command {
61 "boot-recovery" => {
62 let recovery_args = read_null_terminated(&value.recovery);
63 let mut args = vec![];
64 for arg in recovery_args.split_str("\n") {
65 args.push(arg.to_str()?.to_owned());
66 }
67 Ok(BootloaderMessage::BootRecovery(args))
68 }
69 _ => {
70 log_info!("Unrecognized bootloader command {command}");
71 Ok(BootloaderMessage::Unknown)
72 }
73 }
74 }
75}
76
77impl AndroidBootloaderMessageStore {
78 pub fn new(device: &Arc<RemoteBlockDevice>) -> Self {
79 Self(Arc::downgrade(device))
80 }
81
82 pub fn read_bootloader_message(&self) -> Result<BootloaderMessage, Error> {
83 if let Some(device) = self.0.upgrade() {
84 const_assert_eq!(std::mem::size_of::<BootloaderMessageRaw>(), 2048);
85 let mut buf = vec![0u8; 2048];
86 device.read(0, &mut buf)?;
87 BootloaderMessageRaw::read_from_bytes(&buf[..])
88 .map_err(|_| anyhow!("Failed to deserialize bootloader message"))
89 .and_then(|raw| BootloaderMessage::try_from(raw))
90 } else {
91 Err(anyhow!("Can't read bootloader message; device is inaccessible"))
92 }
93 }
94}
95
96pub fn android_bootloader_message_store_init(
101 _locked: &mut Locked<Unlocked>,
102 current_task: &CurrentTask,
103) {
104 let kernel = current_task.kernel().clone();
105 current_task.kernel().remote_block_device_registry.on_device_added(Box::new(
106 move |name, _minor, device| {
107 if name == "misc" {
108 log_info!(
109 "'misc' remote block device detected; setting as bootloader message store"
110 );
111 kernel.expando.get_or_init::<AndroidBootloaderMessageStore>(|| {
112 AndroidBootloaderMessageStore::new(device)
113 });
114 }
115 },
116 ));
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use zerocopy::FromZeros as _;
123
124 #[test]
125 fn test_read_null_terminated() {
126 assert_eq!(read_null_terminated(b""), b"".as_bstr());
127 assert_eq!(read_null_terminated(b"\0"), b"".as_bstr());
128 assert_eq!(read_null_terminated(b"foo\0bar\0"), b"foo".as_bstr());
129 assert_eq!(read_null_terminated(b"foo\0\0"), b"foo".as_bstr());
130 }
131
132 #[test]
133 fn test_parse_bootloader_message() {
134 let mut raw = BootloaderMessageRaw::new_zeroed();
135 raw.command[..14].copy_from_slice(b"boot-recovery\0");
136 raw.recovery[..12].copy_from_slice(b"foo\nbar\nbaz\0");
137 let message = BootloaderMessage::try_from(raw).unwrap();
138 assert_eq!(
139 message,
140 BootloaderMessage::BootRecovery(vec![
141 "foo".to_string(),
142 "bar".to_string(),
143 "baz".to_string()
144 ])
145 );
146
147 let mut raw = BootloaderMessageRaw::new_zeroed();
148 raw.command[..9].copy_from_slice(b"blahblah\0");
149 let message = BootloaderMessage::try_from(raw).unwrap();
150 assert_eq!(message, BootloaderMessage::Unknown);
151
152 let raw = BootloaderMessageRaw::new_zeroed();
153 BootloaderMessage::try_from(raw).unwrap_err();
154 }
155}