Files
s390-tools/rust/cpacfinfo/src/query.rs
Steffen Eiden 8929d21948 rust: Upgrade zerocopy dependency to 0.8.X
This enables some const constructors, Dataful Enums,
Dynamically Sized Types and much more.

v0.8 introduces breaking changes including, but not limited to:
  - Rename AsBytes to IntoBytes
  - Fine-grain (derive) Traits that need to be implemented on top.
  - Rename FromZeroes to FromZeros
for which this patch takes care of as well.

Also a direct FromZeros derive is no longer necessary. As it is touched
anyways, remove it where appropriate.

See: https://github.com/google/zerocopy/discussions/1680

Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-05-05 17:20:37 +02:00

261 lines
8.2 KiB
Rust

// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
use std::fs::File;
use std::io::Error;
use std::io::Read;
use std::ops::Index;
use std::result::Result;
use zerocopy::FromBytes;
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)]
#[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 = File::open(filepath)?;
// 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 = res?;
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));
}
}