rust/cpacfinfo: New CLI tool to provide CPACF information

The new cpacfinfo command provides information about CPACF, such as which CPACF functions are installed, and make use of the new MSA 13 Query Authentication Information function.

Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Reviewed-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
Finn Callies
2024-09-26 08:43:28 +02:00
committed by Steffen Eiden
parent 03340afe88
commit 57a32d98ef
13 changed files with 1569 additions and 0 deletions

26
rust/Cargo.lock generated
View File

@@ -160,6 +160,21 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "cpacfinfo"
version = "0.1.0"
dependencies = [
"anyhow",
"cc",
"clap",
"lazy_static",
"libc",
"serde",
"serde_json",
"utils",
"zerocopy",
]
[[package]]
name = "curl"
version = "0.4.44"
@@ -600,6 +615,17 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_test"
version = "1.0.176"

View File

@@ -1,5 +1,6 @@
[workspace]
members = [
"cpacfinfo",
"pv",
"pv_core",
"pvapconfig",

View File

@@ -17,6 +17,12 @@ ifneq (${HAVE_CARGO},0)
INSTALL_TARGETS := install-rust-tools install-man
CARGO_TEST_TARGETS = $(addsuffix .test, $(CARGO_TARGETS))
ifeq ($(HOST_ARCH),s390x)
CARGO_TARGETS += cpacfinfo
else
BUILD_TARGETS += skip-cpacfinfo
endif #HOSTARCH
ifneq (${HAVE_OPENSSL},0)
ifneq (${HAVE_LIBCURL},0)
PV_TARGETS := pvsecret pvattest
@@ -65,6 +71,9 @@ skip-pv-build:
skip-pvapconfig:
echo " SKIP pvapconfig due to unsupported architecture (s390x only)"
skip-cpacfinfo:
echo " SKIP cpacfinfo due to unsupported architecture (s390x only)"
all: $(BUILD_TARGETS)
install: $(INSTALL_TARGETS)
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)

19
rust/cpacfinfo/Cargo.toml Normal file
View File

@@ -0,0 +1,19 @@
[package]
name = "cpacfinfo"
version = "0.1.0"
edition.workspace = true
license.workspace = true
[dependencies]
anyhow = "1.0.71"
clap = { version = "4.1", features = ["derive"] }
libc = "0.2"
serde = { version = "1.0.164", features = ["derive"] }
serde_json = "1.0"
utils = { path = "../utils" }
zerocopy = { version="0.7", features = ["derive"] }
[build-dependencies]
cc = "1.0"
clap = { version ="4.1", features = ["derive"]}
lazy_static = "1.1"

14
rust/cpacfinfo/build.rs Normal file
View File

@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
fn main() {
#[cfg(target_arch = "s390x")]
cc::Build::new().file("src/stfle.c").compile("stfle");
#[cfg(not(target_arch = "s390x"))]
{
println!("cargo:warning=cpacfinfo will have no functionality on non s390x architectures!");
cc::Build::new().file("src/noop.c").compile("stfle");
}
println!("cargo:rerun-if-changed=src/stfle.c")
}

View File

@@ -0,0 +1,215 @@
.\" Copyright 2024, 2024 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH CPACFINFO 1 "AUG 2024" "s390-tools"
.SH NAME
.B cpacfinfo
\- tool to provide information about CPACF
.SH SYNOPSIS
.B cpacfinfo
[OPTIONS] [<INS>,...] [--format <FORMAT>]
.SH DESCRIPTION
.B cpacfinfo
is able to provide information about the MSA levels supported by the system as well as which
CPACF instructions and functions are available.
.SH OPTIONS
.TP 8
.B \-m/\-\-msa
Displays which MSA levels are enabled and how many functions of the ones introduced by
this level are available. Can be combined with
.B \-f/\-\-functions
to list all functions under the corresponding MSA level. Can be combined with
.B \-i/\-\-instructions
to provide only specific instructions. Can be combined with
.B \-a/\-\-available -n/\-\-not-available
to filter the displayed function. Ignores option
.B \-q/\-\-quiet.
Can be combined with
.B \-\-format
to convert to JSON format.
.TP 8
.B \-f/\-\-functions
Displays subfunctions of CPACF instructions. Can be combined with
.B \-a/\-\-available
and
.B \-n/\-\-not-available
to filter what functions are displayed. By default
.B \-f/\-\-functions
will only provide information about available functions. Therefore
.B cpacfinfo \-f/\-\-functions
and
.B cpacfinfo \-f/\-\-functions \-\-available
show the same output. Use options
.B \-f/\-\-functions \-a/\-\-available \-n/\-\-not-available
together to show information about every function known by the tool. Additionally
.B cpacfinfo
shows available functions not known to the tool as "UNKNOWN".
.TP 8
.B \-i/\-\-instructions <INS>,...
Restricts output of CPACF instructions to be listed.
Multiple instructions can be supplied separated by "," to only show the supplied
instructions in the output. Other options like
.B \-\-quiet \-f/\-\-functions \-m/\-\-msa
or filters like
.B \-a/\-\-available \-n/\-\-not-available
can still be used to further limit output.
.nr PI 2n
Possible values for
.B <INS>
:
.RS
.IP \[bu] 2
.B km
: introduced with MSA
.IP \[bu] 2
.B kmc
: introduced with MSA
.IP \[bu] 2
.B kimd
: introduced with MSA
.IP \[bu] 2
.B klmd
: introduced with MSA
.IP \[bu] 2
.B kmac
: introduced with MSA
.IP \[bu] 2
.B pckmo
: introduced with MSA 3
.IP \[bu] 2
.B kmf
: introduced with MSA 4
.IP \[bu] 2
.B kmctr
: introduced with MSA 4
.IP \[bu] 2
.B kmo
: introduced with MSA 4
.IP \[bu] 2
.B pcc
: introduced with MSA 4
.IP \[bu] 2
.B prno
: introduced with MSA 5
.IP \[bu] 2
.B kma
: introduced with MSA 8
.IP \[bu] 2
.B kdsa
: introduced with MSA 9
.RE
.TP 8
.B \-a/\-\-available
Displays the CPACF subfunctions available on the system.
.B cpacfinfo \-f/\-\-functions
is the same as
.B cpacfinfo \-f/\-\-functions \-a/\-\-available
. If
.B -n/\-\-not-available
is specified no available functions are shown. To get available as well as functions not available use
.B cpacfinfo \-f/\-\-functions \-a/\-\-available \-n/\-\-not-available.
.TP 8
.B \-n/\-\-not-available
Displays the CPACF subfunctions known by the tool that are not-available on the system.
Depending on the Hardware not all MSA levels might be available which can be checked
with the
.B \-m/\-\-msa
option. In such cases it might not be obvious which functions of which instructions are available.
This option together with the
.B \-a/\-\-available
option shows all possible functions.
.TP 8
.B \-q/\-\-quiet
Suppresses the output of the Query Authentication Information.
By default
.B cpacfinfo
outputs the Query Authentication Information for every
Instruction. To keep outputs of other options clean and minimal this can be disabled
with this option.
On machines that do not provide /sys/devices/system/cpu/cpacf/ output will always look like this
option is supplied.
.TP 8
.B \-\-format <FORMAT>
Format output in the specified format. [default: 'human']
If option
.B \-m/\-\-msa
is specified the JSON output will equal to
.B cpacfinfo -m
without any other specified options. If
.B \-m/\-\-msa
is NOT specified the JSON output will equal to
.B cpacfinfo -f
without any other specified options.
.nr PI 2n
Possible values for
.B <FORMAT>
:
.RS
.IP \[bu] 2
.B human
: Use human readable format.
.IP \[bu] 2
.B json
: Use JSON format.
.RE
.TP 8
.B \-v/\-\-version
Print version information and exit.
.TP 8
.B \-h/\-\-help
Print help (see a summary with \-h')
.SH Query Authentication Information
The Query Authentication Information is available since MSA 13 and if it is available
.B cpacfinfo
will display it by default. Query Authentication Information is available for every CPACF
instruction and contains the following:
.nr PI 2n
.RS
.IP \[bu] 2
.B Format
: This format specifies how the binary blob read from sysfs is to be parsed and says nothing
about the Query Authentication Information itself.
.IP \[bu] 2
.B Hash length
: The hash length specifies the length of the hash in bytes.
.IP \[bu] 2
.B IFCL version
: The IFCL version specifies the instruction's firmware code level.
.IP \[bu] 2
.B Hash
: The hash of the firmware code level of the corresponding instruction.
.RE

75
rust/cpacfinfo/src/cli.rs Normal file
View File

@@ -0,0 +1,75 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
use clap::Parser;
use crate::msa::InstructionKind;
#[derive(Clone, PartialEq, clap::ValueEnum, Default)]
pub enum Format {
/// Human-focused, non-parsable output format
#[default]
Human,
/// Use JSON format
Json,
}
/// Command line interface to get information about CP Assist for Cryptographic Functions (CPACF)
#[derive(Parser)]
pub struct Cli {
/// Print version information and exit
#[arg(short, long, exclusive(true))]
pub version: bool,
/// Provide information about the Message Security Assist (MSA)
///
/// Shows which MSA levels are available and how many functions of the ones introduced by
/// this level are available.
/// Compatible with option -f/--functions to list all functions under the corresponding MSA
/// level.
#[arg(short, long, conflicts_with("quiet"))]
pub msa: bool,
/// Shows available functions sorted by instructions
///
/// Provides information about the subfunctions of an instruction.
/// Functions not known to cpacfinfo are displayed as "UNKNOWN".
#[arg(short, long)]
pub functions: bool,
/// Filter instructions to provide in output
///
/// Multiple instructions can be supplied separated by "," to only show the supllied
/// instructions in the output.
#[arg(short, long, num_args = 1.., value_delimiter = ',')]
pub instructions: Vec<InstructionKind>,
/// Shows available functions
///
/// Adds available functions to subfunction output.
#[arg(short, long)]
pub available: bool,
/// Shows functions that are not available
///
/// Adds non-available functions to subfunction output.
#[arg(short, long = "not-available")]
pub not_available: bool,
/// Suppresses the Query Authentication Information output of Instructions
///
/// By default cpacfinfo outputs the Query Authentication Information for every Instruction.
/// To keep outputs of other options clean and minimal this can be disabled with this
/// option.
#[arg(short, long)]
pub quiet: bool,
/// Converts human readable output to JSON format
///
/// Default is human to produce human readable output. When set to json will produce json
/// output.
#[arg(long, value_enum, default_value_t)]
pub format: Format,
}

257
rust/cpacfinfo/src/main.rs Normal file
View File

@@ -0,0 +1,257 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
mod cli;
mod msa;
mod query;
mod stfle;
use anyhow::bail;
use anyhow::Result as anyhowRes;
use clap::Parser;
use std::io::ErrorKind;
use std::result::Result::Ok;
use utils::print_version;
use crate::cli::{Cli, Format};
use crate::msa::*;
use crate::query::*;
use crate::stfle::*;
/// producing -m/--msa output
fn out_msa(args: &Cli, levels: &Vec<MsaLevel>, instructions: &Vec<Instruction>) {
// produce json output
if args.format == Format::Json {
println!("{}", serde_json::to_string(levels).unwrap());
return;
}
// produce human readable output
for lvl in levels {
// print current level
println!("{lvl}");
// if -f/--functions is not specified continue to next level
if !args.functions {
continue;
}
// print all functions introduced by the current level sorted by instruction
for ins in instructions {
// skip instructions for which the current level introduces no new functions
if !args.instructions.contains(&ins.kind) && !args.instructions.is_empty()
|| !ins.info.available
{
continue;
}
// filter all functions that do not fit the command line arguments
let funcs_to_be_printed = ins
.funcs
.iter()
.filter(|func| {
!(!func.available && !args.not_available
|| func.available && !args.available && args.not_available)
})
.filter(|func| func.msa == lvl.msa_level);
// print all functions matching the command line arguments
let mut ins_printed = false;
for func in funcs_to_be_printed {
if !ins_printed {
println!("\t{ins}");
ins_printed = true;
}
println!("\t\t{func}");
}
}
}
}
/// produces output for all cpacfinfo commands that do not contain the -m/--msa flag
fn out_instructions(args: &Cli, instructions: &Vec<Instruction>) {
// produce json output
if args.format == Format::Json {
println!("{}", serde_json::to_string(instructions).unwrap());
return;
}
// produce human readable output
for ins in instructions {
if !args.instructions.contains(&ins.kind) && !args.instructions.is_empty()
|| !ins.info.available
{
continue;
}
println!("{ins}");
// --no-auth-info/-n suppresses the Authentication Information output
if !args.quiet && ins.info.qai_available {
println!("{}", ins.info.qai);
} else if !args.quiet {
println!("Query Authentication Information not available for {ins} instruction! (potentially insufficient machine level)");
}
// --functions/-f lists functions of instructions
if args.functions {
ins.funcs
.iter()
.filter(|func| {
!(!func.available && !args.not_available
|| func.available && !args.available && args.not_available)
})
.for_each(|func| println!("\t{func}"));
println!();
}
}
}
fn main() -> anyhowRes<()> {
/* ---- PARSE COMMAND LINE ARGUMENTS ---- */
let args: Cli = Cli::parse();
/* ---- PRINT VERSION STRING ---- */
if args.version {
print_version!(0, "2024");
return Ok(());
}
/* ---- SET CONSTANTS ---- */
let mut instructions = Vec::new();
init_instructions(&mut instructions);
let mut levels = Vec::new();
for lvl in 0..MSA_LEVEL_COUNT {
let temp = match num2msa(lvl) {
Some(l) => l,
None => panic!("programming error"),
};
let stfle_bit = msa2stfle(&temp);
levels.push(MsaLevel::new(temp, stfle_bit));
let idx_of_last_element = levels.len() - 1;
update_msa_function_count(&args, &mut levels[idx_of_last_element], &instructions);
}
/* ---- GET INFORMATION ---- */
// get stfle bits
let stfle_bits = match Stfle::new() {
Ok(stfle) => stfle,
Err(e) => return Err(e),
};
// check stfle bits for available MSA levels
for lvl in &mut levels {
match lvl.stfle_bit {
Some(bit) => lvl.enabled = stfle_bits.check_bit_in_stfle(bit),
None => continue,
}
}
// check if SYSFS_PATH is available
match check_sysfs() {
true => (),
false => return Ok(()),
}
// run query function (fc 0) for every instruction to check available functions
for ins in &mut instructions {
if stfle_bits.check_bit_in_stfle(ins.info.stfle_bit) {
ins.info.available = true;
// run query; save result in param
let mut param = match query(&ins.kind, QUERY_FUNCTION_CODE) {
Ok(pb) => match pb {
Param::QueryParam(_) => pb,
Param::QaiParam(_) => panic!("programming error"),
},
Err(e) => match e.kind() {
ErrorKind::NotFound => {
println!("Warning: Not able to retrieve subfunction information from sysfs for {ins} instruction");
continue;
}
_ => return Err(e.into()),
},
};
// check if bit for functions of current instruction is set in param
for func in &mut ins.funcs {
if !param.check_bit_in_param(func.function_code as usize) {
continue;
}
func.available = true;
// unset the bit in param to later see if any unsupported functions may be available
param.unset_bit_in_param(func.function_code);
// check if qai is available
if func.function_code == QAI_FUNCTION_CODE {
ins.info.qai_available = true;
}
// sync MsaLevel struct
for lvl in &mut levels {
if lvl.msa_level == func.msa {
lvl.enabled = true;
lvl.available_functions += 1;
if args.instructions.is_empty() || args.instructions.contains(&ins.kind) {
lvl.dynamic_available_functions += 1;
}
break;
}
}
}
// look for any unsupported functions that my be available
for i in 0..NUMBER_FUNC_CODES {
// every bit in param that is 1 is an unsupported function
if !param.check_bit_in_param(i) {
continue;
}
// add function to instruction as UNKNOWN
Instruction::add(ins, Function::new(i as u8, Msa::UNKNOWN, "UNKNOWN"));
// set function as available
match ins.funcs.last_mut() {
Some(ret) => ret.available = true,
None => panic!("programming error"),
}
}
// if query authentication information (fc 127) available run query authentication
// information
if ins.info.qai_available {
// get qai from sysfs
let param = match query(&ins.kind, QAI_FUNCTION_CODE) {
Ok(pb) => match pb {
Param::QueryParam(_) => panic!("programming error"),
Param::QaiParam(_) => pb,
},
Err(e) => match e.kind() {
ErrorKind::NotFound => {
println!("Warning: Not able to retrieve Query Authentication Information from sysfs for {ins} instruction");
continue;
}
_ => return Err(e.into()),
},
};
// parse qai information into QueryAuthInfo struct
match param.parse_qai_based_on_format(&mut ins.info.qai) {
Ok(true) => (),
Ok(false) => println!("WARNING: format {} in query authentication information of instruction {} is UNKNOWN", ins.info.qai.format, ins.kind),
Err(e) => bail!(e.to_string()),
}
}
}
}
/* ---- OUTPUT ---- */
match args.msa {
true => out_msa(&args, &levels, &instructions),
false => out_instructions(&args, &instructions),
}
Ok(())
}

575
rust/cpacfinfo/src/msa.rs Normal file
View File

@@ -0,0 +1,575 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
use crate::cli::Cli;
use core::fmt::{Display, Formatter, Result};
use serde::{Serialize, Serializer};
use utils::HexSlice;
/// Number of total function codes (0 to 127)
pub const NUMBER_FUNC_CODES: usize = 128;
/// Number of MSA levels starting with MSA (0) - MSA 13
pub const MSA_LEVEL_COUNT: u8 = 14;
/// enum of all supported instructions
#[derive(PartialEq, Clone, clap::ValueEnum, Serialize)]
#[allow(clippy::upper_case_acronyms)]
pub enum InstructionKind {
/// introduced with MSA
KM,
/// introduced with MSA
KMC,
/// introduced with MSA
KIMD,
/// introduced with MSA
KLMD,
/// introduced with MSA
KMAC,
/// introduced with MSA 3
PCKMO,
/// introduced with MSA 4
KMF,
/// introduced with MSA 4
KMCTR,
/// introduced with MSA 4
KMO,
/// introduced with MSA 4
PCC,
/// introduced with MSA 5
PRNO,
/// introduced with MSA 8
KMA,
/// introduced with MSA 9
KDSA,
}
/// enum of all MSA levels
#[derive(Clone, Default, PartialEq)]
#[allow(clippy::upper_case_acronyms)]
pub enum Msa {
MSA,
MSA1,
MSA2,
MSA3,
MSA4,
MSA5,
MSA6,
MSA7,
MSA8,
MSA9,
MSA10,
MSA11,
MSA12,
MSA13,
#[default]
UNKNOWN,
}
impl Serialize for Msa {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
Self::MSA => serializer.serialize_unit_variant("Msa", 0, "0"),
Self::MSA1 => serializer.serialize_unit_variant("Msa", 1, "1"),
Self::MSA2 => serializer.serialize_unit_variant("Msa", 2, "2"),
Self::MSA3 => serializer.serialize_unit_variant("Msa", 3, "3"),
Self::MSA4 => serializer.serialize_unit_variant("Msa", 4, "4"),
Self::MSA5 => serializer.serialize_unit_variant("Msa", 5, "5"),
Self::MSA6 => serializer.serialize_unit_variant("Msa", 6, "6"),
Self::MSA7 => serializer.serialize_unit_variant("Msa", 7, "7"),
Self::MSA8 => serializer.serialize_unit_variant("Msa", 8, "8"),
Self::MSA9 => serializer.serialize_unit_variant("Msa", 9, "9"),
Self::MSA10 => serializer.serialize_unit_variant("Msa", 10, "10"),
Self::MSA11 => serializer.serialize_unit_variant("Msa", 11, "11"),
Self::MSA12 => serializer.serialize_unit_variant("Msa", 12, "12"),
Self::MSA13 => serializer.serialize_unit_variant("Msa", 13, "13"),
Self::UNKNOWN => serializer.serialize_unit_variant("Msa", 14, "UNKNOWN"),
}
}
}
impl Display for Msa {
fn fmt(&self, f: &mut Formatter) -> Result {
match *self {
Self::MSA => write!(f, "MSA "),
Self::MSA1 => write!(f, "MSA 1"),
Self::MSA2 => write!(f, "MSA 2"),
Self::MSA3 => write!(f, "MSA 3"),
Self::MSA4 => write!(f, "MSA 4"),
Self::MSA5 => write!(f, "MSA 5"),
Self::MSA6 => write!(f, "MSA 6"),
Self::MSA7 => write!(f, "MSA 7"),
Self::MSA8 => write!(f, "MSA 8"),
Self::MSA9 => write!(f, "MSA 9"),
Self::MSA10 => write!(f, "MSA 10"),
Self::MSA11 => write!(f, "MSA 11"),
Self::MSA12 => write!(f, "MSA 12"),
Self::MSA13 => write!(f, "MSA 13"),
Self::UNKNOWN => write!(f, "UNKNOWN"),
}
}
}
/// converts Instruction enum to a string representation
impl Display for InstructionKind {
fn fmt(&self, f: &mut Formatter) -> Result {
match *self {
Self::KM => write!(f, "KM"),
Self::KMC => write!(f, "KMC"),
Self::KIMD => write!(f, "KIMD"),
Self::KLMD => write!(f, "KLMD"),
Self::KMAC => write!(f, "KMAC"),
Self::PCKMO => write!(f, "PCKMO"),
Self::KMF => write!(f, "KMF"),
Self::KMCTR => write!(f, "KMCTR"),
Self::KMO => write!(f, "KMO"),
Self::PCC => write!(f, "PCC"),
Self::PRNO => write!(f, "PRNO"),
Self::KMA => write!(f, "KMA"),
Self::KDSA => write!(f, "KDSA"),
}
}
}
#[derive(Serialize, Default)]
pub struct MsaLevel {
pub msa_level: Msa,
total_functions: u8,
pub available_functions: u8,
#[serde(skip)]
dynamic_total_functions: u8,
#[serde(skip)]
pub dynamic_available_functions: u8,
pub stfle_bit: Option<u8>,
pub enabled: bool,
}
impl MsaLevel {
pub fn new(msa_level: Msa, stfle_bit: Option<u8>) -> Self {
Self {
msa_level,
stfle_bit,
..Default::default()
}
}
}
impl Display for MsaLevel {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{} ", self.msa_level)?;
match self.stfle_bit {
Some(bit) => write!(f, "STFLE bit [ {:>3} ] : ", bit)?,
None => write!(f, " : ")?,
}
match self.enabled {
true => write!(f, " AVAILABLE")?,
false => write!(f, "NOT AVAILABLE")?,
}
write!(
f,
" ( {:>2} / {:<2} functions available )",
self.dynamic_available_functions, self.dynamic_total_functions
)
}
}
#[derive(Serialize, Clone, Default)]
pub struct Function {
name: String,
pub function_code: u8,
pub available: bool,
#[serde(skip)]
pub msa: Msa,
}
impl Function {
pub fn new(fc: u8, msa: Msa, name: &str) -> Self {
Self {
function_code: fc,
name: name.to_string(),
msa,
..Default::default()
}
}
}
impl Display for Function {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "({:3}) ", self.function_code)?;
match self.available {
true => write!(f, "[ AVAILABLE]")?,
false => write!(f, "[NOT AVAILABLE]")?,
}
write!(f, " {}", self.name)
}
}
#[derive(Serialize, Default)]
pub struct QueryAuthInfo {
pub format: u8,
pub hash_len: u16,
pub version: u32,
// #[serde(with = "hex::serde")]
#[serde(serialize_with = "ser_hex")]
pub hash: Vec<u8>,
}
impl Display for QueryAuthInfo {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, " Format: {}", self.format)?;
if self.format != 0 {
writeln!(f, " (unknown format)")?;
return Ok(());
}
write!(f, "; Hash length: {}", self.hash_len)?;
writeln!(f, "; IFCL version: {}", self.version)?;
writeln!(f, " Hash:")?;
for chunk in self.hash.chunks(16) {
writeln!(f, " {:-}", HexSlice::from(chunk))?;
}
Ok(())
}
}
#[derive(Serialize, Default)]
pub struct InstructionInfo {
pub name: String,
pub available: bool,
pub stfle_bit: u8,
#[serde(skip)]
pub qai_available: bool,
pub qai: QueryAuthInfo,
}
impl InstructionInfo {
fn new(stfle_bit: u8, name: &str) -> Self {
Self {
stfle_bit,
name: name.to_string(),
..Default::default()
}
}
}
#[derive(Serialize)]
pub struct Instruction {
pub kind: InstructionKind,
pub info: InstructionInfo,
pub funcs: Vec<Function>,
}
impl Instruction {
fn new(instruction: InstructionKind, stfle_bit: u8, name: &str) -> Self {
Self {
kind: instruction,
info: InstructionInfo::new(stfle_bit, name),
funcs: Vec::new(),
}
}
pub fn add(&mut self, func: Function) {
self.funcs.push(func);
}
}
impl Display for Instruction {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{} ({})", self.info.name, self.kind)
}
}
/// returns stfle bit based on given MSA level
pub fn msa2stfle(msa_level: &Msa) -> Option<u8> {
match msa_level {
Msa::MSA => Some(17),
Msa::MSA3 => Some(76),
Msa::MSA4 => Some(77),
Msa::MSA5 => Some(57),
Msa::MSA8 => Some(146),
Msa::MSA9 => Some(155),
Msa::MSA12 => Some(86),
_ => None,
}
}
/// returns MSA level based on given u8
pub fn num2msa(num: u8) -> Option<Msa> {
match num {
0 => Some(Msa::MSA),
1 => Some(Msa::MSA1),
2 => Some(Msa::MSA2),
3 => Some(Msa::MSA3),
4 => Some(Msa::MSA4),
5 => Some(Msa::MSA5),
6 => Some(Msa::MSA6),
7 => Some(Msa::MSA7),
8 => Some(Msa::MSA8),
9 => Some(Msa::MSA9),
10 => Some(Msa::MSA10),
11 => Some(Msa::MSA11),
12 => Some(Msa::MSA12),
13 => Some(Msa::MSA13),
_ => None,
}
}
/// Initializes all functions known by cpacfinfo
#[rustfmt::skip]
pub fn init_instructions(instructions: &mut Vec<Instruction>) {
let mut km = Instruction::new(InstructionKind::KM, 17, "Cipher Message");
km.add(Function::new(0, Msa::MSA, "KM-Query"));
km.add(Function::new(1, Msa::MSA, "KM-DEA"));
km.add(Function::new(2, Msa::MSA, "KM-TDEA-128"));
km.add(Function::new(3, Msa::MSA, "KM-TDEA-192"));
km.add(Function::new(9, Msa::MSA3, "KM-Encrypted-DEA"));
km.add(Function::new(10, Msa::MSA3, "KM-Encrypted-TDEA-128"));
km.add(Function::new(11, Msa::MSA3, "KM-Encrypted-TDEA-192"));
km.add(Function::new(18, Msa::MSA1, "KM-AES-128"));
km.add(Function::new(19, Msa::MSA2, "KM-AES-192"));
km.add(Function::new(20, Msa::MSA2, "KM-AES-256"));
km.add(Function::new(26, Msa::MSA3, "KM-Encrypted-AES-128"));
km.add(Function::new(27, Msa::MSA3, "KM-Encrypted-AES-192"));
km.add(Function::new(28, Msa::MSA3, "KM-Encrypted-AES-256"));
km.add(Function::new(50, Msa::MSA4, "KM-XTS-AES-128"));
km.add(Function::new(52, Msa::MSA4, "KM-XTS-AES-256"));
km.add(Function::new(58, Msa::MSA4, "KM-XTS-Encrypted-AES-128"));
km.add(Function::new(60, Msa::MSA4, "KM-XTS-Encrypted-AES-256"));
km.add(Function::new(82, Msa::MSA10, "KM-FULL-XTS-AES-128"));
km.add(Function::new(84, Msa::MSA10, "KM-FULL-XTS-AES-256"));
km.add(Function::new(90, Msa::MSA10, "KM-FULL-XTS-Encrypted-AES-128"));
km.add(Function::new(92, Msa::MSA10, "KM-FULL-XTS-Encrypted-AES-256"));
km.add(Function::new(127, Msa::MSA13, "KM-Query-Authentication-Information"));
let mut kmc = Instruction::new(InstructionKind::KMC, 17, "Cipher Message with Chaining");
kmc.add(Function::new(0, Msa::MSA, "KMC-Query"));
kmc.add(Function::new(1, Msa::MSA, "KMC-DEA"));
kmc.add(Function::new(2, Msa::MSA, "KMC-TDEA-128"));
kmc.add(Function::new(3, Msa::MSA, "KMC-TDEA-192"));
kmc.add(Function::new(9, Msa::MSA3, "KMC-Encrypted-DEA"));
kmc.add(Function::new(10, Msa::MSA3, "KMC-Encrypted-TDEA-128"));
kmc.add(Function::new(11, Msa::MSA3, "KMC-Encrypted-TDEA-192"));
kmc.add(Function::new(18, Msa::MSA1, "KMC-AES-128"));
kmc.add(Function::new(19, Msa::MSA2, "KMC-AES-192"));
kmc.add(Function::new(20, Msa::MSA2, "KMC-AES-256"));
kmc.add(Function::new(26, Msa::MSA3, "KMC-Encrypted-AES-128"));
kmc.add(Function::new(27, Msa::MSA3, "KMC-Encrypted-AES-192"));
kmc.add(Function::new(28, Msa::MSA3, "KMC-Encrypted-AES-256"));
kmc.add(Function::new(67, Msa::MSA1, "KMC-PRNG"));
kmc.add(Function::new(127, Msa::MSA13, "KMC-Query-Authentication-Information"));
let mut kimd = Instruction::new(InstructionKind::KIMD, 17, "Compute Intermediate Message Digest");
kimd.add(Function::new(0, Msa::MSA, "KIMD-Query"));
kimd.add(Function::new(1, Msa::MSA, "KIMD-SHA-1"));
kimd.add(Function::new(2, Msa::MSA1, "KIMD-SHA-256"));
kimd.add(Function::new(3, Msa::MSA2, "KIMD-SHA-512"));
kimd.add(Function::new(32, Msa::MSA6, "KIMD-SHA3-224"));
kimd.add(Function::new(33, Msa::MSA6, "KIMD-SHA3-256"));
kimd.add(Function::new(34, Msa::MSA6, "KIMD-SHA3-384"));
kimd.add(Function::new(35, Msa::MSA6, "KIMD-SHA3-512"));
kimd.add(Function::new(36, Msa::MSA6, "KIMD-SHAKE-128"));
kimd.add(Function::new(37, Msa::MSA6, "KIMD-SHAKE-256"));
kimd.add(Function::new(65, Msa::MSA4, "KIMD-GHASH"));
kimd.add(Function::new(127, Msa::MSA13, "KIMD-Query-Authentication-Information"));
let mut klmd = Instruction::new(InstructionKind::KLMD, 17, "Compute Last Message Digest");
klmd.add(Function::new(0, Msa::MSA, "KLMD-Query"));
klmd.add(Function::new(1, Msa::MSA, "KLMD-SHA-1"));
klmd.add(Function::new(2, Msa::MSA1, "KLMD-SHA-256"));
klmd.add(Function::new(3, Msa::MSA2, "KLMD-SHA-512"));
klmd.add(Function::new(32, Msa::MSA6, "KLMD-SHA3-224"));
klmd.add(Function::new(33, Msa::MSA6, "KLMD-SHA3-256"));
klmd.add(Function::new(34, Msa::MSA6, "KLMD-SHA3-384"));
klmd.add(Function::new(35, Msa::MSA6, "KLMD-SHA3-512"));
klmd.add(Function::new(36, Msa::MSA6, "KLMD-SHAKE-128"));
klmd.add(Function::new(37, Msa::MSA6, "KLMD-SHAKE-256"));
klmd.add(Function::new(127, Msa::MSA13, "KLMD-Query-Authentication-Information"));
let mut kmac = Instruction::new(InstructionKind::KMAC, 17, "Compute Message Authentication Code");
kmac.add(Function::new(0, Msa::MSA, "KMAC-Query"));
kmac.add(Function::new(1, Msa::MSA, "KMAC-DEA"));
kmac.add(Function::new(2, Msa::MSA, "KMAC-TDEA-128"));
kmac.add(Function::new(3, Msa::MSA, "KMAC-TDEA-192"));
kmac.add(Function::new(9, Msa::MSA3, "KMAC-Encrypted-DEA"));
kmac.add(Function::new(10, Msa::MSA3, "KMAC-Encrypted-TDEA-128"));
kmac.add(Function::new(11, Msa::MSA3, "KMAC-Encrypted-TDEA-192"));
kmac.add(Function::new(18, Msa::MSA4, "KMAC-AES-128"));
kmac.add(Function::new(19, Msa::MSA4, "KMAC-AES-192"));
kmac.add(Function::new(20, Msa::MSA4, "KMAC-AES-256"));
kmac.add(Function::new(26, Msa::MSA4, "KMAC-Encrypted-AES-128"));
kmac.add(Function::new(27, Msa::MSA4, "KMAC-Encrypted-AES-192"));
kmac.add(Function::new(28, Msa::MSA4, "KMAC-Encrypted-AES-256"));
kmac.add(Function::new(112, Msa::MSA11, "KMAC-HMAC-SHA-224"));
kmac.add(Function::new(113, Msa::MSA11, "KMAC-HMAC-SHA-256"));
kmac.add(Function::new(114, Msa::MSA11, "KMAC-HMAC-SHA-384"));
kmac.add(Function::new(115, Msa::MSA11, "KMAC-HMAC-SHA-512"));
kmac.add(Function::new(120, Msa::MSA11, "KMAC-HMAC-Encrypted-SHA-224"));
kmac.add(Function::new(121, Msa::MSA11, "KMAC-HMAC-Encrypted-SHA-256"));
kmac.add(Function::new(122, Msa::MSA11, "KMAC-HMAC-Encrypted-SHA-384"));
kmac.add(Function::new(123, Msa::MSA11, "KMAC-HMAC-Encrypted-SHA-512"));
kmac.add(Function::new(127, Msa::MSA13, "KMAC-Query-Authentication-Information"));
let mut pckmo = Instruction::new(InstructionKind::PCKMO, 76, "Perform Cryptographic Key Management Operation");
pckmo.add(Function::new(0, Msa::MSA3, "PCKMO-Query"));
pckmo.add(Function::new(1, Msa::MSA3, "PCKMO-Encrypt-DEA-Key"));
pckmo.add(Function::new(2, Msa::MSA3, "PCKMO-Encrypt-TDEA-128-Key"));
pckmo.add(Function::new(3, Msa::MSA3, "PCKMO-Encrypt-TDEA-192-Key"));
pckmo.add(Function::new(18, Msa::MSA3, "PCKMO-Encrypt-AES-128-Key"));
pckmo.add(Function::new(19, Msa::MSA3, "PCKMO-Encrypt-AES-192-Key"));
pckmo.add(Function::new(20, Msa::MSA3, "PCKMO-Encrypt-AES-256-Key"));
pckmo.add(Function::new(21, Msa::MSA10, "PCKMO-AES-XTS-128-Double"));
pckmo.add(Function::new(22, Msa::MSA10, "PCKMO-AES-XTS-256-Double"));
pckmo.add(Function::new(32, Msa::MSA9, "PCKMO-Encrypt-ECC-P256-Key"));
pckmo.add(Function::new(33, Msa::MSA9, "PCKMO-Encrypt-ECC-P384-Key"));
pckmo.add(Function::new(34, Msa::MSA9, "PCKMO-Encrypt-ECC-P521-Key"));
pckmo.add(Function::new(40, Msa::MSA9, "PCKMO-Encrypt-ECC-Ed25519-Key"));
pckmo.add(Function::new(41, Msa::MSA9, "PCKMO-Encrypt-ECC-Ed448-Key"));
pckmo.add(Function::new(118, Msa::MSA11, "PCKMO-Encrypted-HMAC-512-KEY"));
pckmo.add(Function::new(122, Msa::MSA11, "PCKMO-Encrypted-HMAC-1024-KEY"));
pckmo.add(Function::new(127, Msa::MSA13, "PCKMO-Query-Authentication-Information"));
let mut kmf = Instruction::new(InstructionKind::KMF, 77, "Cipher Message with Cipher Feedback");
kmf.add(Function::new(0, Msa::MSA4, "KMF-Query"));
kmf.add(Function::new(1, Msa::MSA4, "KMF-DEA"));
kmf.add(Function::new(2, Msa::MSA4, "KMF-TDEA-128"));
kmf.add(Function::new(3, Msa::MSA4, "KMF-TDEA-192"));
kmf.add(Function::new(9, Msa::MSA4, "KMF-Encrypted-DEA"));
kmf.add(Function::new(10, Msa::MSA4, "KMF-Encrypted-TDEA-128"));
kmf.add(Function::new(11, Msa::MSA4, "KMF-Encrypted-TDEA-192"));
kmf.add(Function::new(18, Msa::MSA4, "KMF-AES-128"));
kmf.add(Function::new(19, Msa::MSA4, "KMF-AES-192"));
kmf.add(Function::new(20, Msa::MSA4, "KMF-AES-256"));
kmf.add(Function::new(26, Msa::MSA4, "KMF-Encrypted-AES-128"));
kmf.add(Function::new(27, Msa::MSA4, "KMF-Encrypted-AES-192"));
kmf.add(Function::new(28, Msa::MSA4, "KMF-Encrypted-AES-256"));
kmf.add(Function::new(127, Msa::MSA13, "KMF-Query-Authentication-Information"));
let mut kmctr = Instruction::new(InstructionKind::KMCTR, 77, "Cipher Message with Counter");
kmctr.add(Function::new(0, Msa::MSA4, "KMCTR-Query"));
kmctr.add(Function::new(1, Msa::MSA4, "KMCTR-DEA"));
kmctr.add(Function::new(2, Msa::MSA4, "KMCTR-TDEA-128"));
kmctr.add(Function::new(3, Msa::MSA4, "KMCTR-TDEA-192"));
kmctr.add(Function::new(9, Msa::MSA4, "KMCTR-Encrypted-DEA"));
kmctr.add(Function::new(10, Msa::MSA4, "KMCTR-Encrypted-TDEA-128"));
kmctr.add(Function::new(11, Msa::MSA4, "KMCTR-Encrypted-TDEA-192"));
kmctr.add(Function::new(18, Msa::MSA4, "KMCTR-AES-128"));
kmctr.add(Function::new(19, Msa::MSA4, "KMCTR-AES-192"));
kmctr.add(Function::new(20, Msa::MSA4, "KMCTR-AES-256"));
kmctr.add(Function::new(26, Msa::MSA4, "KMCTR-Encrypted-AES-128"));
kmctr.add(Function::new(27, Msa::MSA4, "KMCTR-Encrypted-AES-192"));
kmctr.add(Function::new(28, Msa::MSA4, "KMCTR-Encrypted-AES-256"));
kmctr.add(Function::new(127, Msa::MSA13, "KMCTR-Query-Authentication-Information"));
let mut kmo = Instruction::new(InstructionKind::KMO, 77, "Cipher Message with Output Feedback");
kmo.add(Function::new(0, Msa::MSA4, "KMO-Query"));
kmo.add(Function::new(1, Msa::MSA4, "KMO-DEA"));
kmo.add(Function::new(2, Msa::MSA4, "KMO-TDEA-128"));
kmo.add(Function::new(3, Msa::MSA4, "KMO-TDEA-192"));
kmo.add(Function::new(9, Msa::MSA4, "KMO-Encrypted-DEA"));
kmo.add(Function::new(10, Msa::MSA4, "KMO-Encrypted-TDEA-128"));
kmo.add(Function::new(11, Msa::MSA4, "KMO-Encrypted-TDEA-192"));
kmo.add(Function::new(18, Msa::MSA4, "KMO-AES-128"));
kmo.add(Function::new(19, Msa::MSA4, "KMO-AES-192"));
kmo.add(Function::new(20, Msa::MSA4, "KMO-AES-256"));
kmo.add(Function::new(26, Msa::MSA4, "KMO-Encrypted-AES-128"));
kmo.add(Function::new(27, Msa::MSA4, "KMO-Encrypted-AES-192"));
kmo.add(Function::new(28, Msa::MSA4, "KMO-Encrypted-AES-256"));
kmo.add(Function::new(127, Msa::MSA13, "KMO-Query-Authentication-Information"));
let mut pcc = Instruction::new(InstructionKind::PCC, 77, "Perform Cryptographic Computation");
pcc.add(Function::new(0, Msa::MSA4, "PCC-Query"));
pcc.add(Function::new(1, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-DEA"));
pcc.add(Function::new(2, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-TDEA-128"));
pcc.add(Function::new(3, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-TDEA-192"));
pcc.add(Function::new(9, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-DEA"));
pcc.add(Function::new(10, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-TDEA-128"));
pcc.add(Function::new(11, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-TDEA-192"));
pcc.add(Function::new(18, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-AES-128"));
pcc.add(Function::new(19, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-AES-192"));
pcc.add(Function::new(20, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-AES-256"));
pcc.add(Function::new(26, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-128"));
pcc.add(Function::new(27, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-192"));
pcc.add(Function::new(28, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-256"));
pcc.add(Function::new(50, Msa::MSA4, "PCC-Compute-XTS-Parameter-Using-AES-128"));
pcc.add(Function::new(52, Msa::MSA4, "PCC-Compute-XTS-Parameter-Using-AES-256"));
pcc.add(Function::new(58, Msa::MSA4, "PCC-Compute-XTS-Parameter-Using-Encrypted-AES-128"));
pcc.add(Function::new(60, Msa::MSA4, "PCC-Compute-XTS-Parameter-Using-Encrypted-AES-256"));
pcc.add(Function::new(64, Msa::MSA9, "PCC-Scalar-Multiply-P256"));
pcc.add(Function::new(65, Msa::MSA9, "PCC-Scalar-Multiply-P384"));
pcc.add(Function::new(66, Msa::MSA9, "PCC-Scalar-Multiply-P521"));
pcc.add(Function::new(72, Msa::MSA9, "PCC-Scalar-Multiply-Ed25519"));
pcc.add(Function::new(73, Msa::MSA9, "PCC-Scalar-Multiply-Ed448"));
pcc.add(Function::new(80, Msa::MSA9, "PCC-Scalar-Multiply-X25519"));
pcc.add(Function::new(81, Msa::MSA9, "PCC-Scalar-Multiply-X448"));
pcc.add(Function::new(127, Msa::MSA13, "PCC-Query-Authentication-Information"));
let mut prno = Instruction::new(InstructionKind::PRNO, 57, "Perform Random Number Operation");
prno.add(Function::new(0, Msa::MSA5, "PRNO-Query"));
prno.add(Function::new(3, Msa::MSA5, "PRNO-SHA-512-DRNG"));
prno.add(Function::new(112, Msa::MSA7, "PRNO-TRNG-Query-Raw-to-Conditioned-Ratio"));
prno.add(Function::new(114, Msa::MSA7, "PRNO-TRNG"));
prno.add(Function::new(127, Msa::MSA13, "PRNO-Query-Authentication-Information"));
let mut kma = Instruction::new(InstructionKind::KMA, 146, "Cipher Message with Authentication");
kma.add(Function::new(0, Msa::MSA8, "KMA-Query"));
kma.add(Function::new(18, Msa::MSA8, "KMA-GCM-AES-128"));
kma.add(Function::new(19, Msa::MSA8, "KMA-GCM-AES-192"));
kma.add(Function::new(20, Msa::MSA8, "KMA-GCM-AES-256"));
kma.add(Function::new(26, Msa::MSA8, "KMA-GCM-Encrypted-AES-128"));
kma.add(Function::new(27, Msa::MSA8, "KMA-GCM-Encrypted-AES-192"));
kma.add(Function::new(28, Msa::MSA8, "KMA-GCM-Encrypted-AES-256"));
kma.add(Function::new(127, Msa::MSA13, "KMA-Query-Authentication-Information"));
let mut kdsa = Instruction::new(InstructionKind::KDSA, 155, "Compute Digital Signature Authentication");
kdsa.add(Function::new(0, Msa::MSA9, "KDSA-Query"));
kdsa.add(Function::new(1, Msa::MSA9, "KDSA-ECDSA-Verify-P256"));
kdsa.add(Function::new(2, Msa::MSA9, "KDSA-ECDSA-Verify-P384"));
kdsa.add(Function::new(3, Msa::MSA9, "KDSA-ECDSA-Verify-P521"));
kdsa.add(Function::new(9, Msa::MSA9, "KDSA-ECDSA-Sign-P256"));
kdsa.add(Function::new(10, Msa::MSA9, "KDSA-ECDSA-Sign-P384"));
kdsa.add(Function::new(11, Msa::MSA9, "KDSA-ECDSA-Sign-P521"));
kdsa.add(Function::new(17, Msa::MSA9, "KDSA-Encrypted-ECDSA-Sign-P256"));
kdsa.add(Function::new(18, Msa::MSA9, "KDSA-Encrypted-ECDSA-Sign-P384"));
kdsa.add(Function::new(19, Msa::MSA9, "KDSA-Encrypted-ECDSA-Sign-P521"));
kdsa.add(Function::new(32, Msa::MSA9, "KDSA-EdDSA-Verify-Ed25519"));
kdsa.add(Function::new(36, Msa::MSA9, "KDSA-EdDSA-Verify-Ed448"));
kdsa.add(Function::new(40, Msa::MSA9, "KDSA-EdDSA-Sign-Ed25519"));
kdsa.add(Function::new(44, Msa::MSA9, "KDSA-EdDSA-Sign-Ed448"));
kdsa.add(Function::new(48, Msa::MSA9, "KDSA-Encrypted-EdDSA-Sign-Ed25519"));
kdsa.add(Function::new(52, Msa::MSA9, "KDSA-Encrypted-EdDSA-Sign-Ed448"));
kdsa.add(Function::new(127, Msa::MSA13, "KDSA-Query-Authentication-Information"));
instructions.push(km);
instructions.push(kmc);
instructions.push(kimd);
instructions.push(klmd);
instructions.push(kmac);
instructions.push(pckmo);
instructions.push(kmf);
instructions.push(kmctr);
instructions.push(kmo);
instructions.push(pcc);
instructions.push(prno);
instructions.push(kma);
instructions.push(kdsa);
}
/// number of functions introduced by a level is dynamically counted to ease extension
pub fn update_msa_function_count(args: &Cli, msa: &mut MsaLevel, ins: &Vec<Instruction>) {
for i in ins {
let num_of_funcs_in_level = i.funcs.iter().filter(|f| f.msa == msa.msa_level).count() as u8;
msa.total_functions += num_of_funcs_in_level;
if args.instructions.is_empty() || args.instructions.contains(&i.kind) {
msa.dynamic_total_functions += num_of_funcs_in_level;
}
}
}
pub fn ser_hex<S: Serializer>(data: &Vec<u8>, ser: S) -> std::result::Result<S::Ok, S::Error> {
HexSlice::from(data).serialize(ser)
}

View File

@@ -0,0 +1,8 @@
#include <stdint.h>
#include <stdio.h>
uint32_t stfle(uint64_t __attribute__((unused)) * stfle_fac_list,
uint32_t __attribute__((unused)) size)
{
return 0;
}

268
rust/cpacfinfo/src/query.rs Normal file
View File

@@ -0,0 +1,268 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
use std::fs::File;
use std::io::Error;
use std::io::Read;
use std::io::Result as ioRes;
use std::ops::Index;
use std::result::Result;
use zerocopy::FromBytes;
use zerocopy::FromZeroes;
use crate::msa::InstructionKind;
use crate::msa::QueryAuthInfo;
/// Path to sysfs in which the query and qai informations are fetched from
const SYSFS_PATH: &str = "/sys/devices/system/cpu/cpacf/";
/// Every Instruction has a Query function to get information about what functions are available
pub const QUERY_FUNCTION_CODE: u8 = 0;
/// Number of bytes returned by this Query
pub const QUERY_PARAM_SIZE_IN_BYTES: usize = 16;
/// Starting with MSA 13 every Instruction has a Query Authentication Information function to get
/// information about the running firmware
pub const QAI_FUNCTION_CODE: u8 = 127;
/// Number of bytes returned by this Query Authentication Information
pub const QAI_PARAM_SIZE_IN_BYTES: usize = 256;
/// Query authentication information format identifier
const FORMAT_0: u8 = 0;
#[derive(FromBytes, FromZeroes)]
#[repr(C)]
struct QaiFmt0 {
res00: [u8; 6],
hash_length: u16,
res08: [u8; 4],
version: u32,
hash: [u8; 64],
}
#[allow(clippy::large_enum_variant)]
pub enum Param {
QueryParam([u8; QUERY_PARAM_SIZE_IN_BYTES]),
QaiParam([u8; QAI_PARAM_SIZE_IN_BYTES]),
}
impl Index<u8> for Param {
type Output = u8;
fn index(&self, index: u8) -> &Self::Output {
match self {
Self::QueryParam(p) => &p[index as usize],
Self::QaiParam(p) => &p[index as usize],
}
}
}
impl Param {
pub fn len(&self) -> usize {
match self {
Self::QueryParam(_) => QUERY_PARAM_SIZE_IN_BYTES,
Self::QaiParam(_) => QAI_PARAM_SIZE_IN_BYTES,
}
}
/// check if a specific bit is 1 in param
pub fn check_bit_in_param(&self, check_bit: usize) -> bool {
// get correct byte of param
let byte = check_bit / 8;
// get correct byte of param
if byte >= self.len() {
return false;
}
// get correct bit of param
let bit = 8 - ((check_bit % 8) + 1);
// return if specified bit is set
match self {
Self::QueryParam(param) => (param[byte] & (1 << bit)) > 0,
Self::QaiParam(param) => (param[byte] & (1 << bit)) > 0,
}
}
/// set given bit in param to 0
pub fn unset_bit_in_param(&mut self, flip_bit: u8) {
// get correct byte of param
let byte = flip_bit / 8;
if byte as usize >= self.len() {
return;
}
// get correct bit of param
let bit = 8 - ((flip_bit % 8) + 1);
// build template to logically AND against param byte
// i.e. (flip_bit = 0) template = 1000 0000
let mut template: u8 = 1 << bit;
// flip all bits in template
// i.e. (flip_bit = 0) template = 0111 1111
template = !template;
// set bit to 0 while not changing any other bit
// i.e. (flip_bit = 0) 0111 1111 & xxxx xxxx = 0xxx xxxx
match self {
Self::QueryParam(c) => c[byte as usize] &= template,
Self::QaiParam(c) => c[byte as usize] &= template,
}
}
/// set all bytes of self to value
#[cfg(test)]
pub fn set_param_to(&mut self, value: u8) {
match self {
Self::QueryParam(ref mut content) => *content = [value; QUERY_PARAM_SIZE_IN_BYTES],
Self::QaiParam(ref mut content) => *content = [value; QAI_PARAM_SIZE_IN_BYTES],
}
}
// Outsourced for potential future formats to be easily added in this match statement
pub fn parse_qai_based_on_format(
&self,
qai: &mut QueryAuthInfo,
) -> Result<bool, anyhow::Error> {
match self {
Self::QueryParam(_) => panic!("programming error"),
Self::QaiParam(bin) => {
// The third byte of the param block specifies which format to use to parse the rest
qai.format = bin[3];
// for new formats add a match case here along with a parsing function
match qai.format {
FORMAT_0 => {
parse_qai_format_0(qai, bin);
Ok(true)
}
_ => Ok(false),
}
}
}
}
}
// check if SYSFS_PATH exists
pub fn check_sysfs() -> bool {
match std::path::Path::new(SYSFS_PATH).exists() {
true => true,
false => {
println!("Warning: There seems to be an insufficient kernel level running (sysfs interface {SYSFS_PATH} is missing)\nNo information can be fetched from sysfs, application exits early.");
false
}
}
}
/// parsing the information supplied by sysfs into QueryAuthInfo struct
///
/// The following box shows the qai block with named fields each with a length in bytes.
/// The length of field IFCL Hash depends on IFCL Hash Length and is either 32 or 64 bytes long.
/// In case of a 32 bytes length the latter 32 bytes of the 64 bytes Hash are filled with zeros.
///
/// | BYTE | BYTE | BYTE | BYTE |
/// -----------------------------------------------------------------
/// | RESERVED (3) FORMAT (1) |
/// | RESERVED (2) IFCL HASH LENGTH (2) |
/// | RESERVED (4) |
/// | IFCL VERSION (4) |
/// | IFCL HASH (32 / 64) |
/// | RESERVED (176) |
/// -----------------------------------------------------------------
fn parse_qai_format_0(qai: &mut QueryAuthInfo, param: &[u8]) {
// parse param to temporary struct to ease further conversion
let tmp = QaiFmt0::read_from_prefix(param).expect("programming error");
// parse from temporary struct
qai.hash_len = tmp.hash_length;
qai.version = tmp.version;
// depending on the parsed hash length the hash is parsed
qai.hash = vec![0; qai.hash_len as usize];
qai.hash
.as_mut_slice()
.copy_from_slice(&tmp.hash[..qai.hash_len as usize]);
}
/// cpacfinfo does not execute the actual instruction with function code but uses information
/// provided by the sysfs
pub fn query(ins: &InstructionKind, fc: u8) -> Result<Param, Error> {
// query dependent file names
let auth_info;
let mut param;
match fc {
QUERY_FUNCTION_CODE => {
auth_info = "";
param = Param::QueryParam([0; QUERY_PARAM_SIZE_IN_BYTES]);
}
QAI_FUNCTION_CODE => {
auth_info = "_auth_info";
param = Param::QaiParam([0; QAI_PARAM_SIZE_IN_BYTES]);
}
_ => panic!("programming error"),
};
// depending on which query is performed the bytes to be read from sysfs vary
let bytes_to_be_read = param.len();
// build filepath
let filepath = format!(
"{SYSFS_PATH}{}_query{auth_info}_raw",
ins.to_string().to_lowercase()
);
// open file
let mut f = match File::open(filepath) {
ioRes::Ok(file) => file,
Err(e) => return Err(e),
};
// read file
let res = match param {
Param::QueryParam(ref mut c) => read_file_to_buf(&mut f, c),
Param::QaiParam(ref mut c) => read_file_to_buf(&mut f, c),
};
let bytes_read = match res {
Ok(b) => b,
Err(e) => return Err(e),
};
match bytes_read == bytes_to_be_read {
true => Ok(param),
false => Err(Error::new(std::io::ErrorKind::UnexpectedEof, "test")),
}
}
fn read_file_to_buf(file: &mut File, buf: &mut [u8]) -> Result<usize, Error> {
file.read(buf)
/* match file.read(buf) {
Result::Ok(bytes_read) => Ok(bytes_read),
Err(e) => Err(e),
} */
}
#[cfg(test)]
#[test]
fn test_param_funcs() {
// initialize param with all ones
let mut param = Param::QueryParam([0; QUERY_PARAM_SIZE_IN_BYTES]);
const NUMBER_OF_BITS: usize = 8 * QUERY_PARAM_SIZE_IN_BYTES;
for i in 0..NUMBER_OF_BITS {
// reset param to all ones
param.set_param_to(0xFF);
assert!(param.check_bit_in_param(i));
// set one bit to zero
param.unset_bit_in_param(i as u8);
// check if that bit is zero
assert!(!param.check_bit_in_param(i));
}
}

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
#include <stdint.h>
uint32_t stfle(uint64_t *stfle_fac_list, uint32_t size)
{
uint32_t reg0 = size - 1;
asm volatile(" lgr %%r0,%[reg0]\n"
" .insn s,0xb2b00000,%[list]\n" /* stfle */
" lgr %[reg0],%%r0\n"
: [reg0] "+&d"(reg0), [list] "+Q"(*stfle_fac_list)
:
: "memory", "cc", "r0");
return reg0;
}

View File

@@ -0,0 +1,84 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
use anyhow::Error;
/// Specifies the number of u64 values needed to store the stfle block
pub const STFLE_LEN: usize = 3;
pub struct Stfle {
data: [u64; STFLE_LEN],
}
impl Stfle {
/// Constructs a STFLE block, and saves the STFLE information in the structure.
pub fn new() -> Result<Self, Error> {
let mut ret = Self {
data: [0; STFLE_LEN],
};
// SAFETY: this call is safe because ret can store 64 bits * 3 which equals the required 192
// bits.
let rc = unsafe { stfle(&mut ret.data[0], STFLE_LEN as u32) };
let rc = match rc {
0 => {
println!("Unable to fetch STFLE which is only available on s390x architecture");
return Ok(ret);
}
rc if rc as usize >= STFLE_LEN => STFLE_LEN as u32,
rc => rc + 1,
};
if rc != STFLE_LEN as u32 {
println!("Partial read of STFLE, information might be incomplete");
}
Ok(ret)
}
/// check specific bit in stfle (accounts for big-endianness of stfle)
pub fn check_bit_in_stfle(&self, check_bit: u8) -> bool {
// stfle is big endian while check_bit is little endian
let byte = (check_bit / 64) as usize;
if byte >= STFLE_LEN {
return false;
}
// conversion from little endian check_bit to big endian
let bit = ((check_bit / 64 + 1) * 64 - 1) - check_bit;
self.data[byte] & 1 << bit > 0
}
}
// STFLE bits cannot be retrieved from the system but have to be fetched by running the STFLE
// instruction of Z. This is done in linked C code
extern "C" {
/// Retrieve STFLE bits into list
///
/// List is in big-endian when returned.
/// @list is to return the outcome of the stfle operation. Pointer must be able to store
/// 192 bits.
/// @doublewords specifies the length of the pointer @list as a number of elements behind
/// the pointer.
fn stfle(list: *mut u64, doublewords: u32) -> u32;
}
#[cfg(test)]
#[test]
fn test_check_bit_in_stfle() {
let mut stfle = match Stfle::new() {
Ok(ret) => ret,
Err(e) => panic!("{e}"),
};
for b in 0..(STFLE_LEN * 64) {
// set bit
stfle.data[b / 64] = u64::pow(2, (63 - (b - ((b / 64) * 64))) as u32);
// check if bit is set
assert!(stfle.check_bit_in_stfle(b as u8));
// reset stfle
stfle.data = [0; STFLE_LEN];
}
}