mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
+ Sort and group the imports + Normalize and format comments (100 characters width) Command used: $ cargo +nightly fmt -- Acked-by: Steffen Eiden <seiden@linux.ibm.com> Signed-off-by: Marc Hartmayer <marc@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
383 lines
11 KiB
Rust
383 lines
11 KiB
Rust
// SPDX-License-Identifier: MIT
|
|
//
|
|
// Copyright IBM Corp. 2024
|
|
|
|
use std::fmt::{Debug, Display};
|
|
use std::io::{Read, Seek, SeekFrom};
|
|
|
|
use anyhow::Context;
|
|
use deku::ctx::Endian;
|
|
use deku::{DekuRead, DekuWrite};
|
|
use enum_dispatch::enum_dispatch;
|
|
use pv::request::random_array;
|
|
use pvimg::error::Result;
|
|
use pvimg::secured_comp::ComponentTrait;
|
|
|
|
use self::cmdline::Cmdline;
|
|
use self::kernel::S390Kernel;
|
|
use self::metadata::ImgMetaData;
|
|
use self::ramdisk::Ramdisk;
|
|
use self::sehdr::SeHdrComp;
|
|
use self::shortpsw::ShortPSWComp;
|
|
use self::stage3a::Stage3a;
|
|
use self::stage3b::Stage3b;
|
|
pub use crate::se_img_comps::bootloader::{
|
|
create_ipib, render_stage3a, render_stage3b, stage3a_path, stage3b_path, STAGE3A_ENTRY,
|
|
STAGE3A_INIT_ENTRY, STAGE3A_LOAD_ADDRESS,
|
|
};
|
|
use crate::se_img_comps::ipib::Ipib;
|
|
|
|
mod bootloader;
|
|
|
|
pub mod cmdline;
|
|
pub mod ipib;
|
|
pub mod kernel;
|
|
pub mod metadata;
|
|
pub mod ramdisk;
|
|
pub mod sehdr;
|
|
pub mod shortpsw;
|
|
pub mod stage3a;
|
|
pub mod stage3b;
|
|
|
|
/// A trait for checking a component.
|
|
#[enum_dispatch]
|
|
trait ComponentCheckTrait: ComponentTrait<ComponentKind> {
|
|
/// Check the component
|
|
///
|
|
/// Note: The implementer does not have to care about resetting the file position
|
|
/// as this is done by [`ComponentCheckCtx`].
|
|
fn check(&mut self, ctx: &ComponentCheckCtx) -> Result<()>;
|
|
|
|
/// Initialize [`ComponentCheckCtx`], e.g. it reads what max kernel command
|
|
/// line is supported by the given Linux kernel.
|
|
///
|
|
/// Note: The implementer does not have to care about resetting the file
|
|
/// position as this is done by the [`ComponentCheckCtx`]
|
|
fn init_ctx(&mut self, ctx: &mut ComponentCheckCtx) -> Result<()>;
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct ComponentCheckCtx {
|
|
max_kernel_cmdline_size: usize,
|
|
}
|
|
|
|
impl Default for ComponentCheckCtx {
|
|
fn default() -> Self {
|
|
Self {
|
|
max_kernel_cmdline_size: S390Kernel::LEGACY_MAX_COMMAND_LINE_SIZE,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ComponentCheckCtx {
|
|
fn new() -> Self {
|
|
Default::default()
|
|
}
|
|
|
|
// Initialize component context.
|
|
fn init(&mut self, component: &mut Component) -> Result<()> {
|
|
let old_pos = component.stream_position()?;
|
|
let result = component.init_ctx(self);
|
|
component.seek(SeekFrom::Start(old_pos))?;
|
|
result
|
|
}
|
|
|
|
// Check component.
|
|
fn check_comp(&self, component: &mut Component) -> Result<()> {
|
|
let old_pos = component.stream_position()?;
|
|
let result = component.check(self);
|
|
component.seek(SeekFrom::Start(old_pos))?;
|
|
result
|
|
}
|
|
}
|
|
|
|
/// Check the given components.
|
|
///
|
|
/// The original stream position of the components remains as it was before
|
|
/// calling this function.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// This function will return an error if there was an IO error or the component
|
|
/// check has failed.
|
|
pub fn check_components(components: &mut [Component]) -> Result<(), anyhow::Error> {
|
|
let mut components_ctx = ComponentCheckCtx::new();
|
|
for component in components.iter_mut() {
|
|
components_ctx
|
|
.init(component)
|
|
.with_context(|| format!("Check for {} component has failed", component.kind()))?;
|
|
}
|
|
for component in components.iter_mut() {
|
|
components_ctx
|
|
.check_comp(component)
|
|
.with_context(|| format!("Check for {} component has failed", component.kind()))?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[non_exhaustive]
|
|
#[derive(Debug)]
|
|
#[enum_dispatch(ComponentCheckTrait)]
|
|
pub enum Component {
|
|
ShortPSW(ShortPSWComp),
|
|
ImgMetaData(ImgMetaData),
|
|
Stage3a(Stage3a),
|
|
Kernel(S390Kernel),
|
|
Ramdisk(Ramdisk),
|
|
Cmdline(Cmdline),
|
|
Stage3b(Stage3b),
|
|
SeHdr(SeHdrComp),
|
|
Ipib(Ipib),
|
|
}
|
|
|
|
// No `enum_dispatch` can be used since the trait is implemented in another
|
|
// crate.
|
|
impl Seek for Component {
|
|
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
|
|
match self {
|
|
Self::ShortPSW(obj) => obj.seek(pos),
|
|
Self::Stage3a(obj) => obj.seek(pos),
|
|
Self::Kernel(obj) => obj.seek(pos),
|
|
Self::Ramdisk(obj) => obj.seek(pos),
|
|
Self::Cmdline(obj) => obj.seek(pos),
|
|
Self::Stage3b(obj) => obj.seek(pos),
|
|
Self::SeHdr(obj) => obj.seek(pos),
|
|
Self::Ipib(obj) => obj.seek(pos),
|
|
Self::ImgMetaData(obj) => obj.seek(pos),
|
|
}
|
|
}
|
|
}
|
|
|
|
// No `enum_dispatch` can be used since the trait is implemented in another
|
|
// crate.
|
|
impl Read for Component {
|
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
match self {
|
|
Self::ShortPSW(obj) => obj.read(buf),
|
|
Self::Stage3a(obj) => obj.read(buf),
|
|
Self::Kernel(obj) => obj.read(buf),
|
|
Self::Ramdisk(obj) => obj.read(buf),
|
|
Self::Cmdline(obj) => obj.read(buf),
|
|
Self::Stage3b(obj) => obj.read(buf),
|
|
Self::SeHdr(obj) => obj.read(buf),
|
|
Self::Ipib(obj) => obj.read(buf),
|
|
Self::ImgMetaData(obj) => obj.read(buf),
|
|
}
|
|
}
|
|
}
|
|
|
|
// No `enum_dispatch` can be used since the trait is implemented in another
|
|
// crate.
|
|
impl ComponentTrait<ComponentKind> for Component {
|
|
fn secure_mode(&self) -> bool {
|
|
match self {
|
|
Self::ShortPSW(obj) => obj.secure_mode(),
|
|
Self::Stage3a(obj) => obj.secure_mode(),
|
|
Self::Kernel(obj) => obj.secure_mode(),
|
|
Self::Ramdisk(obj) => obj.secure_mode(),
|
|
Self::Cmdline(obj) => obj.secure_mode(),
|
|
Self::Stage3b(obj) => obj.secure_mode(),
|
|
Self::SeHdr(obj) => obj.secure_mode(),
|
|
Self::Ipib(obj) => obj.secure_mode(),
|
|
Self::ImgMetaData(obj) => obj.secure_mode(),
|
|
}
|
|
}
|
|
|
|
fn kind(&self) -> ComponentKind {
|
|
match self {
|
|
Self::ShortPSW(obj) => obj.kind(),
|
|
Self::Stage3a(obj) => obj.kind(),
|
|
Self::Kernel(obj) => obj.kind(),
|
|
Self::Ramdisk(obj) => obj.kind(),
|
|
Self::Cmdline(obj) => obj.kind(),
|
|
Self::Stage3b(obj) => obj.kind(),
|
|
Self::SeHdr(obj) => obj.kind(),
|
|
Self::Ipib(obj) => obj.kind(),
|
|
Self::ImgMetaData(obj) => obj.kind(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Trick to be able to pass it as `&dyn ReadSeekDebug`
|
|
pub trait ReadSeekDebug: Read + Seek + Debug {}
|
|
impl<T: Read + Seek + Debug> ReadSeekDebug for T {}
|
|
|
|
#[derive(Debug)]
|
|
pub struct CompReader {
|
|
reader: Box<dyn ReadSeekDebug>,
|
|
}
|
|
|
|
impl CompReader {
|
|
pub fn new(reader: Box<dyn ReadSeekDebug>) -> Self {
|
|
Self { reader }
|
|
}
|
|
}
|
|
|
|
impl Read for CompReader {
|
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
self.reader.read(buf)
|
|
}
|
|
}
|
|
|
|
impl Seek for CompReader {
|
|
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
|
|
self.reader.seek(pos)
|
|
}
|
|
}
|
|
|
|
/// The order of enum variants implicitly defines the order of the secured
|
|
/// components within the Secure Execution image!
|
|
#[repr(u16)]
|
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq)]
|
|
pub enum ComponentKind {
|
|
ShortPSW = 10,
|
|
ImgMetaData = 20,
|
|
Stage3a = 30,
|
|
Kernel = 40,
|
|
Ramdisk = 50,
|
|
Cmdline = 60,
|
|
Stage3b = 70,
|
|
SeHdr = 80,
|
|
Ipib = 90,
|
|
}
|
|
|
|
impl ComponentKind {
|
|
pub fn tweak_prefix(&self) -> u16 {
|
|
self.clone() as u16
|
|
}
|
|
|
|
pub fn from_tweak_prefix(value: u16) -> Self {
|
|
// Safety: `value` must correspond to a discriminant value of `Self`
|
|
unsafe { std::mem::transmute(value) }
|
|
}
|
|
}
|
|
|
|
impl Display for ComponentKind {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Self::Kernel => write!(f, "Linux kernel"),
|
|
Self::Ramdisk => write!(f, "ramdisk"),
|
|
Self::Cmdline => write!(f, "kernel cmdline"),
|
|
Self::Stage3a => write!(f, "stage3a"),
|
|
Self::Stage3b => write!(f, "stage3b"),
|
|
Self::SeHdr => write!(f, "Secure Execution header"),
|
|
Self::Ipib => write!(f, "IPIB"),
|
|
Self::ShortPSW => write!(f, "short PSW"),
|
|
Self::ImgMetaData => write!(f, "Image metadata"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, PartialEq, Eq, Clone, DekuRead, DekuWrite)]
|
|
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
|
pub struct CompTweakPrefV1 {
|
|
pub comp_prefix: u16,
|
|
pub rand: [u8; 6],
|
|
}
|
|
impl CompTweakPrefV1 {
|
|
fn to_u64(&self) -> u64 {
|
|
let mut bytes_be = self.comp_prefix.to_be_bytes().to_vec();
|
|
bytes_be.extend_from_slice(self.rand.as_slice());
|
|
assert_eq!(bytes_be.len(), 8);
|
|
// Safety: `bytes_be ` is guaranteed to be 8 bytes long.
|
|
u64::from_be_bytes(bytes_be.try_into().unwrap())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, PartialEq, Eq, Clone, DekuRead, DekuWrite)]
|
|
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
|
pub struct CompTweakV1 {
|
|
pub pref: CompTweakPrefV1,
|
|
pub pg_idx: u64,
|
|
}
|
|
|
|
impl CompTweakV1 {
|
|
pub fn new(kind: ComponentKind) -> Result<Self> {
|
|
let pref = CompTweakPrefV1 {
|
|
comp_prefix: kind.tweak_prefix(),
|
|
rand: random_array()?,
|
|
};
|
|
|
|
Ok(Self { pref, pg_idx: 0 })
|
|
}
|
|
|
|
pub const fn comp_prefix(&self) -> u16 {
|
|
self.pref.comp_prefix
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::shadow_unrelated)]
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use deku::{DekuContainerRead, DekuContainerWrite};
|
|
use proptest::prelude::{Just, Strategy};
|
|
use proptest::{prop_assert_eq, prop_oneof, proptest};
|
|
|
|
use super::{ComponentCheckCtx, ComponentKind};
|
|
use crate::se_img_comps::kernel::S390Kernel;
|
|
use crate::se_img_comps::{check_components, CompTweakPrefV1, CompTweakV1};
|
|
|
|
fn component_kind_strategy() -> impl Strategy<Value = ComponentKind> {
|
|
prop_oneof![
|
|
Just(ComponentKind::ShortPSW),
|
|
Just(ComponentKind::ImgMetaData),
|
|
Just(ComponentKind::Stage3a),
|
|
Just(ComponentKind::Kernel),
|
|
Just(ComponentKind::Ramdisk),
|
|
Just(ComponentKind::Cmdline),
|
|
Just(ComponentKind::Stage3b),
|
|
Just(ComponentKind::SeHdr),
|
|
Just(ComponentKind::Ipib),
|
|
]
|
|
}
|
|
|
|
proptest! {
|
|
#[test]
|
|
fn tweak_prefix_back_to_original(kind in component_kind_strategy()) {
|
|
let prefix = kind.tweak_prefix();
|
|
prop_assert_eq!(kind, ComponentKind::from_tweak_prefix(prefix));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn compctx() {
|
|
let ctx = ComponentCheckCtx::new();
|
|
assert_eq!(
|
|
ctx.max_kernel_cmdline_size,
|
|
S390Kernel::LEGACY_MAX_COMMAND_LINE_SIZE
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_check_components() {
|
|
check_components(&mut []).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn comptweak_v1() {
|
|
let tweak = CompTweakV1 {
|
|
pref: CompTweakPrefV1 {
|
|
comp_prefix: 3,
|
|
rand: [157, 239, 44, 103, 219, 118],
|
|
},
|
|
pg_idx: 0,
|
|
};
|
|
let bytes = [0, 3, 157, 239, 44, 103, 219, 118, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
assert_eq!(tweak.pref.to_u64(), 1018075497880438);
|
|
assert_eq!(tweak.to_bytes().unwrap(), bytes,);
|
|
assert_eq!(CompTweakV1::from_bytes((&bytes, 0)).unwrap().1, tweak);
|
|
|
|
let tweak = CompTweakV1 {
|
|
pref: CompTweakPrefV1 {
|
|
comp_prefix: 0,
|
|
rand: [0; 6],
|
|
},
|
|
pg_idx: 0,
|
|
};
|
|
let bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
assert_eq!(tweak.pref.to_u64(), 0);
|
|
assert_eq!(tweak.to_bytes().unwrap(), bytes,);
|
|
assert_eq!(CompTweakV1::from_bytes((&bytes, 0)).unwrap().1, tweak);
|
|
}
|
|
}
|