1#![cfg_attr(all(not(test), not(doc)), no_std)]
17#[macro_use]
18extern crate log;
19extern crate alloc;
20
21pub mod dev;
22pub mod fs;
23mod mounts;
24mod partition;
25mod root;
26
27pub mod api;
28pub mod fops;
29
30use alloc::{
31 string::{String, ToString},
32 sync::Arc,
33 vec::Vec,
34};
35
36use ax_driver::{AxBlockDevice, AxDeviceContainer, prelude::*};
37
38use crate::partition::PartitionInfo;
39
40pub fn init_filesystems(mut blk_devs: AxDeviceContainer<AxBlockDevice>, bootargs: Option<&str>) {
42 info!("Initialize filesystems...");
43
44 let dev = blk_devs.take_one().expect("No block device found!");
45 info!(" use block device 0: {:?}", dev.device_name());
46 let mut disk = self::dev::Disk::new(dev);
47
48 let root_spec = parse_root_spec(bootargs);
50
51 match self::partition::scan_gpt_partitions(&mut disk) {
53 Ok(partitions) if !partitions.is_empty() => {
54 initialize_with_partitions(disk, partitions, &root_spec)
55 }
56 Ok(_) => {
57 warn!("No partitions found, mount ramfs as rootfs");
58 self::root::init_rootfs_with_ramfs();
59 }
60 Err(e) => {
61 warn!("Failed to scan GPT partitions: {:?}", e);
62 }
63 }
64}
65
66fn initialize_with_partitions(
68 disk: self::dev::Disk,
69 partitions: Vec<PartitionInfo>,
70 root_spec: &RootSpec,
71) {
72 info!(
73 "Found {} partitions, initializing with dynamic filesystem detection",
74 partitions.len()
75 );
76
77 let root_partition_index = find_root_partition(&partitions, root_spec);
79
80 let has_supported_fs = partitions.iter().any(|p| p.filesystem_type.is_some());
82
83 if has_supported_fs {
84 let disk_arc = Arc::new(disk);
86 if !self::root::init_rootfs_with_partitions(disk_arc, partitions, root_partition_index) {
87 warn!("Failed to initialize with partitions.");
88 }
89 } else {
90 warn!("No supported filesystem found in partitions.");
91 }
92}
93
94fn format_guid_as_partuuid(guid: &[u8; 16]) -> alloc::string::String {
96 alloc::format!(
99 "{:02X}{:02X}{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:\
100 02X}{:02X}{:02X}",
101 guid[3],
102 guid[2],
103 guid[1],
104 guid[0], guid[5],
106 guid[4], guid[7],
108 guid[6], guid[8],
110 guid[9], guid[10],
112 guid[11],
113 guid[12],
114 guid[13],
115 guid[14],
116 guid[15] )
118}
119
120#[derive(Debug, Default)]
122struct RootSpec {
123 partition_index: Option<usize>,
124 partuuid: Option<String>,
125 uuid: Option<String>,
126 partlabel: Option<String>,
127}
128
129fn parse_root_spec(bootargs: Option<&str>) -> RootSpec {
131 let mut spec = RootSpec::default();
132
133 if let Some(bootargs) = bootargs
134 && let Some(root_arg) = bootargs
135 .split_whitespace()
136 .find(|arg| arg.starts_with("root="))
137 {
138 let root_value = root_arg.strip_prefix("root=").unwrap_or("");
139
140 spec = match root_value {
141 v if v.starts_with("/dev/sda") => parse_device_path(v, "/dev/sda"),
142 v if v.starts_with("/dev/mmcblk") => parse_mmcblk_path(v),
143 v if v.starts_with("PARTUUID=") => {
144 let partuuid = v.strip_prefix("PARTUUID=").unwrap_or("").to_uppercase();
145 info!("Looking for partition with PARTUUID: {}", partuuid);
146 RootSpec {
147 partuuid: Some(partuuid),
148 ..Default::default()
149 }
150 }
151 v if v.starts_with("UUID=") => {
152 let uuid = v.strip_prefix("UUID=").unwrap_or("").to_uppercase();
153 info!("Looking for filesystem with UUID: {}", uuid);
154 RootSpec {
155 uuid: Some(uuid),
156 ..Default::default()
157 }
158 }
159 v if v.starts_with("PARTLABEL=") => {
160 let partlabel = v.strip_prefix("PARTLABEL=").unwrap_or("").to_string();
161 info!("Looking for partition with PARTLABEL: {}", partlabel);
162 RootSpec {
163 partlabel: Some(partlabel),
164 ..Default::default()
165 }
166 }
167 _ => spec,
168 };
169 }
170
171 spec
172}
173
174fn parse_device_path(path: &str, prefix: &str) -> RootSpec {
176 if let Some(part_num) = path.strip_prefix(prefix)
177 && let Ok(num) = part_num.parse::<usize>()
178 && num > 0
179 {
180 return RootSpec {
181 partition_index: Some(num - 1),
182 ..Default::default()
183 };
184 }
185 RootSpec::default()
186}
187
188fn parse_mmcblk_path(path: &str) -> RootSpec {
190 if let Some(remaining) = path.strip_prefix("/dev/mmcblk")
191 && let Some(p_pos) = remaining.find('p')
192 {
193 let part_str = &remaining[p_pos + 1..];
194 if let Ok(num) = part_str.parse::<usize>()
195 && num > 0
196 {
197 return RootSpec {
198 partition_index: Some(num - 1),
199 ..Default::default()
200 };
201 }
202 }
203 RootSpec::default()
204}
205
206fn find_root_partition(partitions: &[PartitionInfo], root_spec: &RootSpec) -> Option<usize> {
208 if let Some(index) = root_spec.partition_index {
210 return if index < partitions.len() {
211 Some(index)
212 } else {
213 None
214 };
215 }
216
217 for (i, partition) in partitions.iter().enumerate() {
219 if partition.filesystem_type.is_none() {
220 continue;
221 }
222
223 if let Some(ref partuuid) = root_spec.partuuid {
225 let partition_guid = format_guid_as_partuuid(&partition.unique_partition_guid);
226 debug!("Partition {} PARTUUID: {}", i, partition_guid);
227 if partition_guid.contains(partuuid) {
228 info!("Found matching partition by PARTUUID: {}", i);
229 return Some(i);
230 }
231 }
232
233 if let Some(ref uuid) = root_spec.uuid
235 && let Some(ref partition_uuid) = partition.filesystem_uuid
236 && partition_uuid.to_uppercase() == *uuid
237 {
238 info!("UUID matches partition {} ({})", i, partition.name);
239 return Some(i);
240 }
241
242 if let Some(ref partlabel) = root_spec.partlabel
244 && partition.name == *partlabel
245 {
246 info!("PARTLABEL matches partition {} ({})", i, partition.name);
247 return Some(i);
248 }
249 }
250
251 None
252}