1#![cfg_attr(not(feature = "std"), no_std)]
2
3extern crate alloc;
4
5use alloc::collections::BTreeMap;
6use alloc::string::String;
7use alloc::vec::Vec;
8pub use gibblox_pipeline::{
9 PipelineSource as BootProfileArtifactSource,
10 PipelineSourceAndroidSparseImgSource as BootProfileArtifactSourceAndroidSparseImgSource,
11 PipelineSourceCasync as BootProfileArtifactSourceCasync,
12 PipelineSourceCasyncSource as BootProfileArtifactSourceCasyncSource,
13 PipelineSourceFileSource as BootProfileArtifactSourceFileSource,
14 PipelineSourceGpt as BootProfileArtifactSourceGpt,
15 PipelineSourceGptSource as BootProfileArtifactSourceGptSource,
16 PipelineSourceHttpSource as BootProfileArtifactSourceHttpSource,
17 PipelineSourceMbr as BootProfileArtifactSourceMbr,
18 PipelineSourceMbrSource as BootProfileArtifactSourceMbrSource,
19 PipelineSourceTar as BootProfileArtifactSourceTar,
20 PipelineSourceTarSource as BootProfileArtifactSourceTarSource,
21 PipelineSourceXzSource as BootProfileArtifactSourceXzSource,
22};
23use serde::{Deserialize, Serialize};
24
25#[cfg(feature = "schema")]
26use schemars::JsonSchema;
27
28#[derive(Clone, Debug, Deserialize, Serialize)]
30#[cfg_attr(feature = "schema", derive(JsonSchema))]
31#[serde(deny_unknown_fields)]
32pub struct DeviceProfile {
33 pub id: String,
34 pub display_name: Option<String>,
35 pub devicetree_name: String,
36 pub r#match: Vec<MatchRule>,
37 pub probe: Vec<ProbeStep>,
38 pub boot: Boot,
39}
40
41#[derive(Clone, Debug, Deserialize, Serialize)]
42#[cfg_attr(feature = "schema", derive(JsonSchema))]
43pub struct MatchRule {
44 pub fastboot: FastbootMatch,
45}
46
47#[derive(Clone, Debug, Deserialize, Serialize)]
48#[cfg_attr(feature = "schema", derive(JsonSchema))]
49pub struct FastbootMatch {
50 pub vid: u16,
51 pub pid: u16,
52}
53
54#[derive(Clone, Debug, Deserialize, Serialize)]
55#[cfg_attr(feature = "schema", derive(JsonSchema))]
56pub enum ProbeStep {
57 #[serde(untagged)]
58 FastbootGetvarEq(FastbootGetvarEq),
59 #[serde(untagged)]
60 FastbootGetvarStartsWith(FastbootGetvarStartsWith),
61 #[serde(untagged)]
62 FastbootGetvarNotEq(FastbootGetvarNotEq),
63 #[serde(untagged)]
64 FastbootGetvarExists(FastbootGetvarExists),
65 #[serde(untagged)]
66 FastbootGetvarNotExists(FastbootGetvarNotExists),
67}
68
69#[derive(Clone, Debug, Deserialize, Serialize)]
70#[cfg_attr(feature = "schema", derive(JsonSchema))]
71pub struct FastbootGetvarEq {
72 #[serde(rename = "fastboot.getvar")]
73 pub name: String,
74 pub equals: String,
75}
76
77#[derive(Clone, Debug, Deserialize, Serialize)]
78#[cfg_attr(feature = "schema", derive(JsonSchema))]
79pub struct FastbootGetvarStartsWith {
80 #[serde(rename = "fastboot.getvar")]
81 pub name: String,
82 pub starts_with: String,
83}
84
85#[derive(Clone, Debug, Deserialize, Serialize)]
86#[cfg_attr(feature = "schema", derive(JsonSchema))]
87pub struct FastbootGetvarNotEq {
88 #[serde(rename = "fastboot.getvar")]
89 pub name: String,
90 pub not_equals: String,
91}
92
93#[derive(Clone, Debug, Deserialize, Serialize)]
94#[cfg_attr(feature = "schema", derive(JsonSchema))]
95pub struct FastbootGetvarExists {
96 #[serde(rename = "fastboot.getvar")]
97 pub name: String,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
100 pub exists: Option<ExistsFlag>,
101}
102
103#[derive(Clone, Debug, Deserialize, Serialize)]
104#[cfg_attr(feature = "schema", derive(JsonSchema))]
105pub struct FastbootGetvarNotExists {
106 #[serde(rename = "fastboot.getvar")]
107 pub name: String,
108 #[serde(default, skip_serializing_if = "Option::is_none")]
110 pub not_exists: Option<NotExistsFlag>,
111}
112
113#[derive(Clone, Debug, Deserialize, Serialize)]
114#[cfg_attr(feature = "schema", derive(JsonSchema))]
115pub struct ExistsFlag;
116
117#[derive(Clone, Debug, Deserialize, Serialize)]
118#[cfg_attr(feature = "schema", derive(JsonSchema))]
119pub struct NotExistsFlag;
120
121#[derive(Clone, Debug, Deserialize, Serialize)]
122#[cfg_attr(feature = "schema", derive(JsonSchema))]
123pub struct Boot {
124 pub fastboot_boot: BootPayload,
125}
126
127#[derive(Clone, Debug, Deserialize, Serialize)]
128#[cfg_attr(feature = "schema", derive(JsonSchema))]
129pub struct BootPayload {
130 #[serde(alias = "bootimg")]
131 pub android_bootimg: AndroidBootImage,
132}
133
134#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
135#[cfg_attr(feature = "schema", derive(JsonSchema))]
136pub struct InjectMac {
137 #[serde(default, skip_serializing_if = "Option::is_none")]
138 pub wifi: Option<String>,
139 #[serde(default, skip_serializing_if = "Option::is_none")]
140 pub bluetooth: Option<String>,
141}
142
143#[derive(Clone, Debug, Deserialize, Serialize)]
144#[cfg_attr(feature = "schema", derive(JsonSchema))]
145pub struct AndroidBootImage {
146 pub header_version: u32,
147 pub page_size: u32,
148 #[serde(default)]
149 pub base: Option<u64>,
150 #[serde(default)]
151 pub kernel_offset: Option<u64>,
152 #[serde(default)]
153 pub dtb_offset: Option<u64>,
154 #[serde(default)]
155 pub limits: Option<BootLimits>,
156 pub kernel: AndroidKernel,
157 #[serde(default)]
158 pub initrd: Option<AndroidInitrd>,
159 #[serde(default)]
160 pub cmdline_append: Option<String>,
161}
162
163#[derive(Clone, Debug, Deserialize, Serialize)]
164#[cfg_attr(feature = "schema", derive(JsonSchema))]
165pub struct BootLimits {
166 pub max_kernel_bytes: Option<u64>,
167 pub max_initrd_bytes: Option<u64>,
168 pub max_total_bytes: Option<u64>,
169}
170
171#[derive(Clone, Debug, Deserialize, Serialize)]
172#[cfg_attr(feature = "schema", derive(JsonSchema))]
173pub struct AndroidKernel {
174 pub encoding: KernelEncoding,
175}
176
177#[derive(Clone, Debug, Deserialize, Serialize)]
178#[cfg_attr(feature = "schema", derive(JsonSchema))]
179#[serde(rename_all = "kebab-case")]
180pub enum KernelEncoding {
181 #[serde(rename = "image")]
182 Image,
183 #[serde(rename = "image+dtb")]
184 ImageDtb,
185 #[serde(rename = "image.gz")]
186 ImageGzip,
187 #[serde(rename = "image.gz+dtb")]
188 ImageGzipDtb,
189 #[serde(rename = "image.lz4")]
190 ImageLz4,
191 #[serde(rename = "image.lz4+dtb")]
192 ImageLz4Dtb,
193 #[serde(rename = "image.zst")]
194 ImageZstd,
195 #[serde(rename = "image.zst+dtb")]
196 ImageZstdDtb,
197}
198
199impl KernelEncoding {
200 pub fn compression(&self) -> Compression {
201 match self {
202 KernelEncoding::Image | KernelEncoding::ImageDtb => Compression::None,
203 KernelEncoding::ImageGzip | KernelEncoding::ImageGzipDtb => Compression::Gzip,
204 KernelEncoding::ImageLz4 | KernelEncoding::ImageLz4Dtb => Compression::Lz4,
205 KernelEncoding::ImageZstd | KernelEncoding::ImageZstdDtb => Compression::Zstd,
206 }
207 }
208
209 pub fn append_dtb(&self) -> bool {
210 matches!(
211 self,
212 KernelEncoding::ImageDtb
213 | KernelEncoding::ImageGzipDtb
214 | KernelEncoding::ImageLz4Dtb
215 | KernelEncoding::ImageZstdDtb
216 )
217 }
218}
219
220#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
221#[cfg_attr(feature = "schema", derive(JsonSchema))]
222#[serde(rename_all = "kebab-case")]
223pub enum Compression {
224 None,
225 Gzip,
226 Lz4,
227 Zstd,
228}
229
230#[derive(Clone, Debug, Deserialize, Serialize)]
231#[cfg_attr(feature = "schema", derive(JsonSchema))]
232pub struct AndroidInitrd {
233 #[serde(default)]
234 pub compress: Option<Compression>,
235}
236
237#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
242#[cfg_attr(feature = "schema", derive(JsonSchema))]
243#[serde(deny_unknown_fields)]
244pub struct BootProfileManifest {
245 pub id: String,
246 #[serde(default, skip_serializing_if = "Option::is_none")]
247 pub display_name: Option<String>,
248 pub rootfs: BootProfileRootfs,
249 #[serde(default, skip_serializing_if = "Option::is_none")]
250 pub kernel: Option<BootProfileArtifactPathSource>,
251 #[serde(default, skip_serializing_if = "Option::is_none")]
252 pub dtbs: Option<BootProfileArtifactPathSource>,
253 #[serde(default, skip_serializing_if = "Vec::is_empty")]
254 pub dt_overlays: Vec<String>,
255 #[serde(default, skip_serializing_if = "Option::is_none")]
256 pub extra_cmdline: Option<String>,
257 #[serde(default, skip_serializing_if = "BootProfileManifestStage0::is_empty")]
258 pub stage0: BootProfileManifestStage0,
259}
260
261impl BootProfileManifest {
262 pub fn compile_dt_overlays<E, F>(&self, mut compile: F) -> Result<BootProfile, E>
263 where
264 F: FnMut(&str) -> Result<Vec<u8>, E>,
265 {
266 let mut dt_overlays = Vec::with_capacity(self.dt_overlays.len());
267 for overlay in &self.dt_overlays {
268 dt_overlays.push(compile(overlay)?);
269 }
270
271 let mut devices = BTreeMap::new();
272 for (device_id, device) in &self.stage0.devices {
273 let mut device_overlays = Vec::with_capacity(device.dt_overlays.len());
274 for overlay in &device.dt_overlays {
275 device_overlays.push(compile(overlay)?);
276 }
277 devices.insert(
278 device_id.clone(),
279 BootProfileDevice {
280 dt_overlays: device_overlays,
281 extra_cmdline: device.extra_cmdline.clone(),
282 stage0: BootProfileDeviceStage0 {
283 kernel_modules: device.stage0.kernel_modules.clone(),
284 inject_mac: device.stage0.inject_mac.clone(),
285 },
286 },
287 );
288 }
289
290 Ok(BootProfile {
291 id: self.id.clone(),
292 display_name: self.display_name.clone(),
293 rootfs: self.rootfs.clone(),
294 kernel: self.kernel.clone(),
295 dtbs: self.dtbs.clone(),
296 dt_overlays,
297 extra_cmdline: self.extra_cmdline.clone(),
298 stage0: BootProfileStage0 {
299 kernel_modules: self.stage0.kernel_modules.clone(),
300 devices,
301 },
302 })
303 }
304}
305
306#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
307#[cfg_attr(feature = "schema", derive(JsonSchema))]
308#[serde(deny_unknown_fields)]
309pub struct BootProfileManifestStage0 {
310 #[serde(default, skip_serializing_if = "Vec::is_empty")]
311 pub kernel_modules: Vec<String>,
312 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
313 pub devices: BTreeMap<String, BootProfileManifestDevice>,
314}
315
316impl BootProfileManifestStage0 {
317 pub fn is_empty(&self) -> bool {
318 self.kernel_modules.is_empty() && self.devices.is_empty()
319 }
320}
321
322#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
323#[cfg_attr(feature = "schema", derive(JsonSchema))]
324#[serde(deny_unknown_fields)]
325pub struct BootProfileManifestDevice {
326 #[serde(default, skip_serializing_if = "Vec::is_empty")]
327 pub dt_overlays: Vec<String>,
328 #[serde(default, skip_serializing_if = "Option::is_none")]
329 pub extra_cmdline: Option<String>,
330 #[serde(
331 default,
332 skip_serializing_if = "BootProfileManifestDeviceStage0::is_empty"
333 )]
334 pub stage0: BootProfileManifestDeviceStage0,
335}
336
337#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
338#[cfg_attr(feature = "schema", derive(JsonSchema))]
339#[serde(deny_unknown_fields)]
340pub struct BootProfileManifestDeviceStage0 {
341 #[serde(default, skip_serializing_if = "Vec::is_empty")]
342 pub kernel_modules: Vec<String>,
343 #[serde(default, skip_serializing_if = "Option::is_none")]
344 pub inject_mac: Option<InjectMac>,
345}
346
347impl BootProfileManifestDeviceStage0 {
348 pub fn is_empty(&self) -> bool {
349 self.kernel_modules.is_empty() && self.inject_mac.is_none()
350 }
351}
352
353#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
355#[cfg_attr(feature = "schema", derive(JsonSchema))]
356#[serde(deny_unknown_fields)]
357pub struct BootProfile {
358 pub id: String,
359 #[serde(default, skip_serializing_if = "Option::is_none")]
360 pub display_name: Option<String>,
361 pub rootfs: BootProfileRootfs,
362 #[serde(default, skip_serializing_if = "Option::is_none")]
363 pub kernel: Option<BootProfileArtifactPathSource>,
364 #[serde(default, skip_serializing_if = "Option::is_none")]
365 pub dtbs: Option<BootProfileArtifactPathSource>,
366 #[serde(default, skip_serializing_if = "Vec::is_empty")]
367 pub dt_overlays: Vec<Vec<u8>>,
368 #[serde(default, skip_serializing_if = "Option::is_none")]
369 pub extra_cmdline: Option<String>,
370 #[serde(default, skip_serializing_if = "BootProfileStage0::is_empty")]
371 pub stage0: BootProfileStage0,
372}
373
374impl BootProfile {
375 pub fn decompile_dt_overlays<E, F>(&self, mut decompile: F) -> Result<BootProfileManifest, E>
376 where
377 F: FnMut(&[u8]) -> Result<String, E>,
378 {
379 let mut dt_overlays = Vec::with_capacity(self.dt_overlays.len());
380 for overlay in &self.dt_overlays {
381 dt_overlays.push(decompile(overlay)?);
382 }
383
384 let mut devices = BTreeMap::new();
385 for (device_id, device) in &self.stage0.devices {
386 let mut device_overlays = Vec::with_capacity(device.dt_overlays.len());
387 for overlay in &device.dt_overlays {
388 device_overlays.push(decompile(overlay)?);
389 }
390 devices.insert(
391 device_id.clone(),
392 BootProfileManifestDevice {
393 dt_overlays: device_overlays,
394 extra_cmdline: device.extra_cmdline.clone(),
395 stage0: BootProfileManifestDeviceStage0 {
396 kernel_modules: device.stage0.kernel_modules.clone(),
397 inject_mac: device.stage0.inject_mac.clone(),
398 },
399 },
400 );
401 }
402
403 Ok(BootProfileManifest {
404 id: self.id.clone(),
405 display_name: self.display_name.clone(),
406 rootfs: self.rootfs.clone(),
407 kernel: self.kernel.clone(),
408 dtbs: self.dtbs.clone(),
409 dt_overlays,
410 extra_cmdline: self.extra_cmdline.clone(),
411 stage0: BootProfileManifestStage0 {
412 kernel_modules: self.stage0.kernel_modules.clone(),
413 devices,
414 },
415 })
416 }
417}
418
419#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
420#[cfg_attr(feature = "schema", derive(JsonSchema))]
421#[serde(deny_unknown_fields)]
422pub struct BootProfileStage0 {
423 #[serde(default, skip_serializing_if = "Vec::is_empty")]
424 pub kernel_modules: Vec<String>,
425 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
426 pub devices: BTreeMap<String, BootProfileDevice>,
427}
428
429impl BootProfileStage0 {
430 pub fn is_empty(&self) -> bool {
431 self.kernel_modules.is_empty() && self.devices.is_empty()
432 }
433}
434
435#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
436#[cfg_attr(feature = "schema", derive(JsonSchema))]
437#[serde(deny_unknown_fields)]
438pub struct BootProfileDevice {
439 #[serde(default, skip_serializing_if = "Vec::is_empty")]
440 pub dt_overlays: Vec<Vec<u8>>,
441 #[serde(default, skip_serializing_if = "Option::is_none")]
442 pub extra_cmdline: Option<String>,
443 #[serde(default, skip_serializing_if = "BootProfileDeviceStage0::is_empty")]
444 pub stage0: BootProfileDeviceStage0,
445}
446
447#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
448#[cfg_attr(feature = "schema", derive(JsonSchema))]
449#[serde(deny_unknown_fields)]
450pub struct BootProfileDeviceStage0 {
451 #[serde(default, skip_serializing_if = "Vec::is_empty")]
452 pub kernel_modules: Vec<String>,
453 #[serde(default, skip_serializing_if = "Option::is_none")]
454 pub inject_mac: Option<InjectMac>,
455}
456
457impl BootProfileDeviceStage0 {
458 pub fn is_empty(&self) -> bool {
459 self.kernel_modules.is_empty() && self.inject_mac.is_none()
460 }
461}
462
463#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
464#[cfg_attr(feature = "schema", derive(JsonSchema))]
465#[serde(untagged)]
466pub enum BootProfileRootfs {
467 Ostree(BootProfileRootfsOstreeSource),
468 Erofs(BootProfileRootfsErofsSource),
469 Ext4(BootProfileRootfsExt4Source),
470 Fat(BootProfileRootfsFatSource),
471}
472
473impl BootProfileRootfs {
474 pub fn source(&self) -> &BootProfileArtifactSource {
475 match self {
476 Self::Ostree(source) => source.source(),
477 Self::Erofs(source) => &source.erofs,
478 Self::Ext4(source) => &source.ext4,
479 Self::Fat(source) => &source.fat,
480 }
481 }
482
483 pub fn source_mut(&mut self) -> &mut BootProfileArtifactSource {
484 match self {
485 Self::Ostree(source) => source.source_mut(),
486 Self::Erofs(source) => &mut source.erofs,
487 Self::Ext4(source) => &mut source.ext4,
488 Self::Fat(source) => &mut source.fat,
489 }
490 }
491
492 pub fn is_ostree(&self) -> bool {
493 matches!(self, Self::Ostree(_))
494 }
495}
496
497#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
498#[cfg_attr(feature = "schema", derive(JsonSchema))]
499#[serde(deny_unknown_fields)]
500pub struct BootProfileRootfsOstreeSource {
501 pub ostree: BootProfileRootfsFilesystemSource,
502}
503
504impl BootProfileRootfsOstreeSource {
505 pub fn source(&self) -> &BootProfileArtifactSource {
506 self.ostree.source()
507 }
508
509 pub fn source_mut(&mut self) -> &mut BootProfileArtifactSource {
510 self.ostree.source_mut()
511 }
512}
513
514#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
515#[cfg_attr(feature = "schema", derive(JsonSchema))]
516#[serde(untagged)]
517pub enum BootProfileRootfsFilesystemSource {
518 Erofs(BootProfileRootfsErofsSource),
519 Ext4(BootProfileRootfsExt4Source),
520 Fat(BootProfileRootfsFatSource),
521}
522
523impl BootProfileRootfsFilesystemSource {
524 pub fn source(&self) -> &BootProfileArtifactSource {
525 match self {
526 Self::Erofs(source) => &source.erofs,
527 Self::Ext4(source) => &source.ext4,
528 Self::Fat(source) => &source.fat,
529 }
530 }
531
532 pub fn source_mut(&mut self) -> &mut BootProfileArtifactSource {
533 match self {
534 Self::Erofs(source) => &mut source.erofs,
535 Self::Ext4(source) => &mut source.ext4,
536 Self::Fat(source) => &mut source.fat,
537 }
538 }
539}
540
541#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
542#[cfg_attr(feature = "schema", derive(JsonSchema))]
543pub struct BootProfileArtifactPathSource {
544 pub path: String,
545 #[serde(flatten)]
546 pub source: BootProfileRootfs,
547}
548
549impl BootProfileArtifactPathSource {
550 pub fn artifact_source(&self) -> &BootProfileArtifactSource {
551 self.source.source()
552 }
553
554 pub fn artifact_source_mut(&mut self) -> &mut BootProfileArtifactSource {
555 self.source.source_mut()
556 }
557}
558
559#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
560#[cfg_attr(feature = "schema", derive(JsonSchema))]
561#[serde(deny_unknown_fields)]
562pub struct BootProfileRootfsErofsSource {
563 pub erofs: BootProfileArtifactSource,
564}
565
566#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
567#[cfg_attr(feature = "schema", derive(JsonSchema))]
568#[serde(deny_unknown_fields)]
569pub struct BootProfileRootfsExt4Source {
570 pub ext4: BootProfileArtifactSource,
571}
572
573#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
574#[cfg_attr(feature = "schema", derive(JsonSchema))]
575#[serde(deny_unknown_fields)]
576pub struct BootProfileRootfsFatSource {
577 pub fat: BootProfileArtifactSource,
578}
579
580pub mod bin;