diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e8d4a4b..02e9e5a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,3 +18,7 @@ jobs: run: | make + - name: Run cargo + run: | + cd degpt + cargo build diff --git a/degpt/.gitignore b/degpt/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/degpt/.gitignore @@ -0,0 +1 @@ +/target diff --git a/degpt/Cargo.lock b/degpt/Cargo.lock new file mode 100644 index 0000000..fb37120 --- /dev/null +++ b/degpt/Cargo.lock @@ -0,0 +1,88 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "degpt" +version = "0.1.0" +dependencies = [ + "gpt", + "uuid", +] + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gpt" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8283e7331b8c93b9756e0cfdbcfb90312852f953c6faf9bf741e684cc3b6ad69" +dependencies = [ + "bitflags", + "crc", + "log", + "uuid", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/degpt/Cargo.toml b/degpt/Cargo.toml new file mode 100644 index 0000000..f69421b --- /dev/null +++ b/degpt/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "degpt" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +gpt = "3.1" +uuid = "1.8" diff --git a/degpt/LICENCE b/degpt/LICENCE new file mode 100644 index 0000000..8a941aa --- /dev/null +++ b/degpt/LICENCE @@ -0,0 +1,47 @@ +MIT License + +Copyright (c) 2024 Linaro Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------ + +The GPT implementation is based on the gpt crate: + +MIT License + +Copyright (c) 2017 Quyzi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/degpt/src/lib.rs b/degpt/src/lib.rs new file mode 100644 index 0000000..552b02c --- /dev/null +++ b/degpt/src/lib.rs @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2024 Linaro Ltd. + +pub mod mygpt; diff --git a/degpt/src/main.rs b/degpt/src/main.rs new file mode 100644 index 0000000..a69da9b --- /dev/null +++ b/degpt/src/main.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2024 Linaro Ltd. + +use std::env; +use std::error::Error; +use std::fs; +use std::fs::File; +use std::io::Write; + +use degpt::mygpt::*; + +fn main() -> Result<(), Box> { + let mut args = env::args().skip(1); + + let mut xml; + if let Some(name) = args.next() { + xml = File::create(name)?; + } else { + return Err(format!( + "usage: {} partition.xml gpt_both*.bin", + env::args().next().unwrap() + ) + .into()); + } + + xml.write_all( + concat!( + "\n", + "\n", + " \n", + " WRITE_PROTECT_BOUNDARY_IN_KB=0\n", + " SECTOR_SIZE_IN_BYTES = 4096\n", + " GROW_LAST_PARTITION_TO_FILL_DISK=true\n", + " \n", + "\n", + ) + .as_bytes(), + )?; + + for gpt in args { + let bytes = fs::read(&gpt)?; + let hdr = Header::try_from(&bytes[0x1000..0x2000])?; + + println!("{gpt}"); + println!("{hdr:#}"); + + xml.write_all(" \n".as_bytes())?; + for i in 0..hdr.num_parts { + let offset = 0x2000 + i as usize * 0x80; + let part = Partition::try_from(&bytes[offset..offset + 0x80])?; + if part.part_guid.is_nil() { + continue; + } + println!("{part:#}"); + let size = (part.last_lba + 1 - part.first_lba) * 4096 / 1024; + let readonly: bool = part.flags & (1 << 60) != 0; + xml.write_all( + format!( + " \n", + part.name, + size, + part.part_type_guid.guid.to_string().to_uppercase(), + readonly, + ) + .as_bytes(), + )?; + } + + xml.write_all(" \n\n".as_bytes())?; + } + + xml.write_all("\n".as_bytes())?; + + Ok(()) +} diff --git a/degpt/src/mygpt.rs b/degpt/src/mygpt.rs new file mode 100644 index 0000000..168e868 --- /dev/null +++ b/degpt/src/mygpt.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2024 Linaro Ltd. + +pub mod header; +pub mod partition; + +pub use header::Header; +pub use partition::Partition; +pub use partition::Type; diff --git a/degpt/src/mygpt/header.rs b/degpt/src/mygpt/header.rs new file mode 100644 index 0000000..e2f3a84 --- /dev/null +++ b/degpt/src/mygpt/header.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2017 Quyzi +// Copyright (c) 2024 Linaro Ltd. + +use std::error::Error; +use std::fmt; +use std::io::Cursor; + +use gpt::header::parse_uuid; + +/// Header describing a GPT disk. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Header { + /// GPT header magic signature, hardcoded to "EFI PART". + pub signature: String, // Offset 0. "EFI PART", 45h 46h 49h 20h 50h 41h 52h 54h + /// 00 00 01 00 + pub revision: u32, // Offset 8 + /// little endian + pub header_size_le: u32, // Offset 12 + /// CRC32 of the header with crc32 section zeroed + pub crc32: u32, // Offset 16 + /// must be 0 + pub reserved: u32, // Offset 20 + /// For main header, 1 + pub current_lba: u64, // Offset 24 + /// LBA for backup header + pub backup_lba: u64, // Offset 32 + /// First usable LBA for partitions (primary table last LBA + 1) + pub first_usable: u64, // Offset 40 + /// Last usable LBA (secondary partition table first LBA - 1) + pub last_usable: u64, // Offset 48 + /// UUID of the disk + pub disk_guid: uuid::Uuid, // Offset 56 + /// Starting LBA of partition entries + pub part_start: u64, // Offset 72 + /// Number of partition entries + pub num_parts: u32, // Offset 80 + /// Size of a partition entry, usually 128 + pub part_size: u32, // Offset 84 + /// CRC32 of the partition table + pub crc32_parts: u32, // Offset 88 +} + +impl fmt::Display for Header { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Disk:\t\t{}\nCRC32:\t\t{}\nTable CRC:\t{}", + self.disk_guid, self.crc32, self.crc32_parts + ) + } +} + +impl TryFrom<&[u8]> for Header { + type Error = Box; + + fn try_from(buf: &[u8]) -> Result { + Ok(Header { + signature: String::from_utf8(buf[0..8].into())?, + revision: u32::from_le_bytes(buf[8..12].try_into()?), + header_size_le: u32::from_le_bytes(buf[12..16].try_into()?), + crc32: u32::from_le_bytes(buf[16..20].try_into()?), + reserved: u32::from_le_bytes(buf[20..24].try_into()?), + current_lba: u64::from_le_bytes(buf[24..32].try_into()?), + backup_lba: u64::from_le_bytes(buf[32..40].try_into()?), + first_usable: u64::from_le_bytes(buf[40..48].try_into()?), + last_usable: u64::from_le_bytes(buf[48..56].try_into()?), + disk_guid: parse_uuid(&mut Cursor::new(&buf[56..72]))?, + part_start: u64::from_le_bytes(buf[72..80].try_into()?), + num_parts: u32::from_le_bytes(buf[80..84].try_into()?), + part_size: u32::from_le_bytes(buf[84..88].try_into()?), + crc32_parts: u32::from_le_bytes(buf[88..92].try_into()?), + }) + } +} diff --git a/degpt/src/mygpt/partition.rs b/degpt/src/mygpt/partition.rs new file mode 100644 index 0000000..3cd1efd --- /dev/null +++ b/degpt/src/mygpt/partition.rs @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2017 Quyzi +// Copyright (c) 2024 Linaro Ltd. +use std::error::Error; +use std::fmt; +use std::io::Cursor; + +use gpt::header::parse_uuid; +use gpt::partition_types::OperatingSystem; + +/// The type +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Type { + /// Type-GUID for a GPT partition. + pub guid: uuid::Uuid, + /// well-known OS label for this type-GUID. + pub os: OperatingSystem, +} + +/// A partition entry in a GPT partition table. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Partition { + /// GUID of the partition type. + pub part_type_guid: Type, + /// UUID of the partition. + pub part_guid: uuid::Uuid, + /// First LBA of the partition. + pub first_lba: u64, + /// Last LBA of the partition. + pub last_lba: u64, + /// Partition flags. + pub flags: u64, + /// Partition name. + pub name: String, +} + +impl fmt::Display for Partition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Partition:\t\t{}\nPartition GUID:\t\t{}\nPartition Type:\t\t{}\n\ + Span:\t\t\t{} - {}\nFlags:\t\t\t{}", + self.name, + self.part_guid, + self.part_type_guid.guid, + self.first_lba, + self.last_lba, + self.flags, + ) + } +} + +fn get_utf16le(buf: &[u8]) -> Vec { + let mut r = vec![]; + for c in buf.chunks(2) { + let u = c[0] as u16 | ((c[1] as u16) << 8); + if u != 0 { + r.push(u); + } + } + r +} + +impl TryFrom<&[u8]> for Partition { + type Error = Box; + + fn try_from(buf: &[u8]) -> Result { + Ok(Partition { + part_type_guid: Type { + guid: parse_uuid(&mut Cursor::new(&buf[0..]))?, + os: OperatingSystem::None, + }, + part_guid: parse_uuid(&mut Cursor::new(&buf[16..]))?, + first_lba: u64::from_le_bytes(buf[32..40].try_into()?), + last_lba: u64::from_le_bytes(buf[40..48].try_into()?), + flags: u64::from_le_bytes(buf[48..56].try_into()?), + name: String::from_utf16(&get_utf16le(&buf[56..128]))?, + }) + } +}