// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 mod cli; mod msa; mod query; mod stfle; use std::io::ErrorKind; use std::result::Result::Ok; use anyhow::{bail, Result as anyhowRes}; use clap::Parser; 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, instructions: &Vec) { // 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) { // 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!("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 = Stfle::new()?; // 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(()) }