#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
pub use gibblox_pipeline::{
PipelineSource as BootProfileArtifactSource,
PipelineSourceAndroidSparseImgSource as BootProfileArtifactSourceAndroidSparseImgSource,
PipelineSourceCasync as BootProfileArtifactSourceCasync,
PipelineSourceCasyncSource as BootProfileArtifactSourceCasyncSource,
PipelineSourceFileSource as BootProfileArtifactSourceFileSource,
PipelineSourceGpt as BootProfileArtifactSourceGpt,
PipelineSourceGptSource as BootProfileArtifactSourceGptSource,
PipelineSourceHttpSource as BootProfileArtifactSourceHttpSource,
PipelineSourceMbr as BootProfileArtifactSourceMbr,
PipelineSourceMbrSource as BootProfileArtifactSourceMbrSource,
PipelineSourceTar as BootProfileArtifactSourceTar,
PipelineSourceTarSource as BootProfileArtifactSourceTarSource,
PipelineSourceXzSource as BootProfileArtifactSourceXzSource,
};
use serde::{Deserialize, Serialize};
#[cfg(feature = "schema")]
use schemars::JsonSchema;
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct DeviceProfile {
pub id: String,
pub display_name: Option<String>,
pub devicetree_name: String,
pub r#match: Vec<MatchRule>,
pub probe: Vec<ProbeStep>,
pub boot: Boot,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct MatchRule {
pub fastboot: FastbootMatch,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct FastbootMatch {
pub vid: u16,
pub pid: u16,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub enum ProbeStep {
#[serde(untagged)]
FastbootGetvarEq(FastbootGetvarEq),
#[serde(untagged)]
FastbootGetvarStartsWith(FastbootGetvarStartsWith),
#[serde(untagged)]
FastbootGetvarNotEq(FastbootGetvarNotEq),
#[serde(untagged)]
FastbootGetvarExists(FastbootGetvarExists),
#[serde(untagged)]
FastbootGetvarNotExists(FastbootGetvarNotExists),
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct FastbootGetvarEq {
#[serde(rename = "fastboot.getvar")]
pub name: String,
pub equals: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct FastbootGetvarStartsWith {
#[serde(rename = "fastboot.getvar")]
pub name: String,
pub starts_with: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct FastbootGetvarNotEq {
#[serde(rename = "fastboot.getvar")]
pub name: String,
pub not_equals: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct FastbootGetvarExists {
#[serde(rename = "fastboot.getvar")]
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exists: Option<ExistsFlag>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct FastbootGetvarNotExists {
#[serde(rename = "fastboot.getvar")]
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub not_exists: Option<NotExistsFlag>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct ExistsFlag;
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct NotExistsFlag;
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct Boot {
pub fastboot_boot: BootPayload,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct BootPayload {
#[serde(alias = "bootimg")]
pub android_bootimg: AndroidBootImage,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct InjectMac {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub wifi: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bluetooth: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct AndroidBootImage {
pub header_version: u32,
pub page_size: u32,
#[serde(default)]
pub base: Option<u64>,
#[serde(default)]
pub kernel_offset: Option<u64>,
#[serde(default)]
pub dtb_offset: Option<u64>,
#[serde(default)]
pub limits: Option<BootLimits>,
pub kernel: AndroidKernel,
#[serde(default)]
pub initrd: Option<AndroidInitrd>,
#[serde(default)]
pub cmdline_append: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct BootLimits {
pub max_kernel_bytes: Option<u64>,
pub max_initrd_bytes: Option<u64>,
pub max_total_bytes: Option<u64>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct AndroidKernel {
pub encoding: KernelEncoding,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
pub enum KernelEncoding {
#[serde(rename = "image")]
Image,
#[serde(rename = "image+dtb")]
ImageDtb,
#[serde(rename = "image.gz")]
ImageGzip,
#[serde(rename = "image.gz+dtb")]
ImageGzipDtb,
#[serde(rename = "image.lz4")]
ImageLz4,
#[serde(rename = "image.lz4+dtb")]
ImageLz4Dtb,
#[serde(rename = "image.zst")]
ImageZstd,
#[serde(rename = "image.zst+dtb")]
ImageZstdDtb,
}
impl KernelEncoding {
pub fn compression(&self) -> Compression {
match self {
KernelEncoding::Image | KernelEncoding::ImageDtb => Compression::None,
KernelEncoding::ImageGzip | KernelEncoding::ImageGzipDtb => Compression::Gzip,
KernelEncoding::ImageLz4 | KernelEncoding::ImageLz4Dtb => Compression::Lz4,
KernelEncoding::ImageZstd | KernelEncoding::ImageZstdDtb => Compression::Zstd,
}
}
pub fn append_dtb(&self) -> bool {
matches!(
self,
KernelEncoding::ImageDtb
| KernelEncoding::ImageGzipDtb
| KernelEncoding::ImageLz4Dtb
| KernelEncoding::ImageZstdDtb
)
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
pub enum Compression {
None,
Gzip,
Lz4,
Zstd,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct AndroidInitrd {
#[serde(default)]
pub compress: Option<Compression>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct BootProfileManifest {
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
pub rootfs: BootProfileRootfs,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kernel: Option<BootProfileArtifactPathSource>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dtbs: Option<BootProfileArtifactPathSource>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub dt_overlays: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra_cmdline: Option<String>,
#[serde(default, skip_serializing_if = "BootProfileManifestStage0::is_empty")]
pub stage0: BootProfileManifestStage0,
}
impl BootProfileManifest {
pub fn compile_dt_overlays<E, F>(&self, mut compile: F) -> Result<BootProfile, E>
where
F: FnMut(&str) -> Result<Vec<u8>, E>,
{
let mut dt_overlays = Vec::with_capacity(self.dt_overlays.len());
for overlay in &self.dt_overlays {
dt_overlays.push(compile(overlay)?);
}
let mut devices = BTreeMap::new();
for (device_id, device) in &self.stage0.devices {
let mut device_overlays = Vec::with_capacity(device.dt_overlays.len());
for overlay in &device.dt_overlays {
device_overlays.push(compile(overlay)?);
}
devices.insert(
device_id.clone(),
BootProfileDevice {
dt_overlays: device_overlays,
extra_cmdline: device.extra_cmdline.clone(),
stage0: BootProfileDeviceStage0 {
kernel_modules: device.stage0.kernel_modules.clone(),
inject_mac: device.stage0.inject_mac.clone(),
},
},
);
}
Ok(BootProfile {
id: self.id.clone(),
display_name: self.display_name.clone(),
rootfs: self.rootfs.clone(),
kernel: self.kernel.clone(),
dtbs: self.dtbs.clone(),
dt_overlays,
extra_cmdline: self.extra_cmdline.clone(),
stage0: BootProfileStage0 {
kernel_modules: self.stage0.kernel_modules.clone(),
devices,
},
})
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct BootProfileManifestStage0 {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub kernel_modules: Vec<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub devices: BTreeMap<String, BootProfileManifestDevice>,
}
impl BootProfileManifestStage0 {
pub fn is_empty(&self) -> bool {
self.kernel_modules.is_empty() && self.devices.is_empty()
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct BootProfileManifestDevice {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub dt_overlays: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra_cmdline: Option<String>,
#[serde(
default,
skip_serializing_if = "BootProfileManifestDeviceStage0::is_empty"
)]
pub stage0: BootProfileManifestDeviceStage0,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct BootProfileManifestDeviceStage0 {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub kernel_modules: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub inject_mac: Option<InjectMac>,
}
impl BootProfileManifestDeviceStage0 {
pub fn is_empty(&self) -> bool {
self.kernel_modules.is_empty() && self.inject_mac.is_none()
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct BootProfile {
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
pub rootfs: BootProfileRootfs,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kernel: Option<BootProfileArtifactPathSource>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dtbs: Option<BootProfileArtifactPathSource>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub dt_overlays: Vec<Vec<u8>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra_cmdline: Option<String>,
#[serde(default, skip_serializing_if = "BootProfileStage0::is_empty")]
pub stage0: BootProfileStage0,
}
impl BootProfile {
pub fn decompile_dt_overlays<E, F>(&self, mut decompile: F) -> Result<BootProfileManifest, E>
where
F: FnMut(&[u8]) -> Result<String, E>,
{
let mut dt_overlays = Vec::with_capacity(self.dt_overlays.len());
for overlay in &self.dt_overlays {
dt_overlays.push(decompile(overlay)?);
}
let mut devices = BTreeMap::new();
for (device_id, device) in &self.stage0.devices {
let mut device_overlays = Vec::with_capacity(device.dt_overlays.len());
for overlay in &device.dt_overlays {
device_overlays.push(decompile(overlay)?);
}
devices.insert(
device_id.clone(),
BootProfileManifestDevice {
dt_overlays: device_overlays,
extra_cmdline: device.extra_cmdline.clone(),
stage0: BootProfileManifestDeviceStage0 {
kernel_modules: device.stage0.kernel_modules.clone(),
inject_mac: device.stage0.inject_mac.clone(),
},
},
);
}
Ok(BootProfileManifest {
id: self.id.clone(),
display_name: self.display_name.clone(),
rootfs: self.rootfs.clone(),
kernel: self.kernel.clone(),
dtbs: self.dtbs.clone(),
dt_overlays,
extra_cmdline: self.extra_cmdline.clone(),
stage0: BootProfileManifestStage0 {
kernel_modules: self.stage0.kernel_modules.clone(),
devices,
},
})
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct BootProfileStage0 {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub kernel_modules: Vec<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub devices: BTreeMap<String, BootProfileDevice>,
}
impl BootProfileStage0 {
pub fn is_empty(&self) -> bool {
self.kernel_modules.is_empty() && self.devices.is_empty()
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct BootProfileDevice {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub dt_overlays: Vec<Vec<u8>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra_cmdline: Option<String>,
#[serde(default, skip_serializing_if = "BootProfileDeviceStage0::is_empty")]
pub stage0: BootProfileDeviceStage0,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct BootProfileDeviceStage0 {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub kernel_modules: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub inject_mac: Option<InjectMac>,
}
impl BootProfileDeviceStage0 {
pub fn is_empty(&self) -> bool {
self.kernel_modules.is_empty() && self.inject_mac.is_none()
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged)]
pub enum BootProfileRootfs {
Ostree(BootProfileRootfsOstreeSource),
Erofs(BootProfileRootfsErofsSource),
Ext4(BootProfileRootfsExt4Source),
Fat(BootProfileRootfsFatSource),
}
impl BootProfileRootfs {
pub fn source(&self) -> &BootProfileArtifactSource {
match self {
Self::Ostree(source) => source.source(),
Self::Erofs(source) => &source.erofs,
Self::Ext4(source) => &source.ext4,
Self::Fat(source) => &source.fat,
}
}
pub fn source_mut(&mut self) -> &mut BootProfileArtifactSource {
match self {
Self::Ostree(source) => source.source_mut(),
Self::Erofs(source) => &mut source.erofs,
Self::Ext4(source) => &mut source.ext4,
Self::Fat(source) => &mut source.fat,
}
}
pub fn is_ostree(&self) -> bool {
matches!(self, Self::Ostree(_))
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct BootProfileRootfsOstreeSource {
pub ostree: BootProfileRootfsFilesystemSource,
}
impl BootProfileRootfsOstreeSource {
pub fn source(&self) -> &BootProfileArtifactSource {
self.ostree.source()
}
pub fn source_mut(&mut self) -> &mut BootProfileArtifactSource {
self.ostree.source_mut()
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged)]
pub enum BootProfileRootfsFilesystemSource {
Erofs(BootProfileRootfsErofsSource),
Ext4(BootProfileRootfsExt4Source),
Fat(BootProfileRootfsFatSource),
}
impl BootProfileRootfsFilesystemSource {
pub fn source(&self) -> &BootProfileArtifactSource {
match self {
Self::Erofs(source) => &source.erofs,
Self::Ext4(source) => &source.ext4,
Self::Fat(source) => &source.fat,
}
}
pub fn source_mut(&mut self) -> &mut BootProfileArtifactSource {
match self {
Self::Erofs(source) => &mut source.erofs,
Self::Ext4(source) => &mut source.ext4,
Self::Fat(source) => &mut source.fat,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct BootProfileArtifactPathSource {
pub path: String,
#[serde(flatten)]
pub source: BootProfileRootfs,
}
impl BootProfileArtifactPathSource {
pub fn artifact_source(&self) -> &BootProfileArtifactSource {
self.source.source()
}
pub fn artifact_source_mut(&mut self) -> &mut BootProfileArtifactSource {
self.source.source_mut()
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct BootProfileRootfsErofsSource {
pub erofs: BootProfileArtifactSource,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct BootProfileRootfsExt4Source {
pub ext4: BootProfileArtifactSource,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct BootProfileRootfsFatSource {
pub fat: BootProfileArtifactSource,
}
pub mod bin;