diff --git a/exts/devmaster/Cargo.toml b/exts/devmaster/Cargo.toml index f5f2a587c50194f5e1214d2f4c17b4b9015872d2..2a7ee09408f784dfddd55591877cca627d637f77 100644 --- a/exts/devmaster/Cargo.toml +++ b/exts/devmaster/Cargo.toml @@ -18,6 +18,18 @@ path = "src/bin/devctl/main.rs" name = "ata_id" path = "src/bin/tools/ata_id/main.rs" +[[bin]] +name = "fido_id" +path = "src/bin/tools/fido_id/main.rs" + +[[bin]] +name = "v4l_id" +path = "src/bin/tools/v4l_id/main.rs" + +[[bin]] +name = "mtd_probe" +path = "src/bin/tools/mtd_probe/main.rs" + [lib] name = "libdevmaster" path = "src/lib/lib.rs" diff --git a/exts/devmaster/src/bin/tools/fido_id/main.rs b/exts/devmaster/src/bin/tools/fido_id/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..7041f71214832a0942556252a622056e47996f2a --- /dev/null +++ b/exts/devmaster/src/bin/tools/fido_id/main.rs @@ -0,0 +1,160 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! fido_id + +use device::Device; +use log::logger; +use std::fs::OpenOptions; +use std::os::unix::prelude::{FileExt, OpenOptionsExt}; +use std::process; + +const HID_RPTDESC_FIRST_BYTE_LONG_ITEM: u8 = 0xfe; +const HID_RPTDESC_TYPE_GLOBAL: u8 = 0x1; +const HID_RPTDESC_TYPE_LOCAL: u8 = 0x2; +const HID_RPTDESC_TAG_USAGE_PAGE: u8 = 0x0; +const HID_RPTDESC_TAG_USAGE: u8 = 0x0; +const FIDO_FULL_USAGE_CTAPHID: u32 = 0xf1d00001; + +fn main() { + logger::init_log_to_console("fido_id", log::Level::Info); + let args: Vec = std::env::args().collect(); + if args.len() > 2 { + log::error!("Usage: fido_id [SYSFS_PATH]: {}.", nix::Error::EINVAL); + process::exit(libc::EINVAL); + } + + let sys_path = match report_descriptor_path(if args.len() == 1 { "" } else { &args[1] }) { + Ok(path) => path, + Err(err) => { + log::error!("Failed to get sys path: {}.", err); + process::exit(err.get_errno() as libc::c_int); + } + }; + + let mut buf = [0_u8; 4096]; + let size = match read_report_descriptor(&sys_path, &mut buf) { + Ok(size) => size, + Err(err) => { + log::error!("Failed to read {}: {}.", sys_path, err); + process::exit(err.raw_os_error().unwrap_or_default()); + } + }; + + if is_fido_security_token_desc(&buf[..size]) { + println!("ID_FIDO_TOKEN=1"); + println!("ID_SECURITY_TOKEN=1"); + } else { + log::error!("Failed to parse report descriptor at {}.", sys_path) + } + process::exit(0); +} + +fn get_env_str() -> String { + let mut ret = String::new(); + for (key, value) in std::env::vars() { + ret.push_str(&format!("{}={}\0", key, value)); + } + ret +} + +fn report_descriptor_path(syspath: &str) -> std::result::Result { + let device = if syspath.is_empty() { + Device::from_nulstr(get_env_str().as_bytes())? + } else { + Device::from_syspath(syspath, true)? + }; + + let hid_device = device.get_parent()?; + let mut sys_path = hid_device.borrow().get_syspath()?; + sys_path.push_str("/report_descriptor"); + Ok(sys_path) +} + +fn read_report_descriptor(syspath: &str, buf: &mut [u8]) -> std::io::Result { + let file = OpenOptions::new() + .custom_flags(libc::O_NOFOLLOW | libc::O_CLOEXEC | libc::O_NOCTTY) + .read(true) + .open(syspath)?; + file.read_at(buf, 0) +} + +#[allow(unused_assignments)] +fn is_fido_security_token_desc(desc: &[u8]) -> bool { + let mut usage = 0_u32; + let mut pos = 0_usize; + let desc_len = desc.len(); + while pos < desc_len { + let mut tag = 0_u8; + let mut type_ = 0_u8; + let mut size_code = 0_u8; + let mut size = 0_usize; + let mut value = 0_u32; + + /* Report descriptors consists of short items (1-5 bytes) and long items (3-258 bytes). */ + if desc[pos] == HID_RPTDESC_FIRST_BYTE_LONG_ITEM { + /* No long items are defined in the spec; skip them. + * The length of the data in a long item is contained in the byte after the long + * item tag. The header consists of three bytes: special long item tag, length, + * actual tag. */ + if pos + 1 >= desc.len() { + return false; + } + pos += (desc[pos + 1] + 3) as usize; + continue; + } + /* The first byte of a short item encodes tag, type and size. */ + tag = desc[pos] >> 4; /* Bits 7 to 4 */ + type_ = (desc[pos] >> 2) & 0x3; /* Bits 3 and 2 */ + size_code = desc[pos] & 0x3; /* Bits 1 and 0 */ + /* Size is coded as follows: + * 0 -> 0 bytes, 1 -> 1 byte, 2 -> 2 bytes, 3 -> 4 bytes + */ + size = if size_code < 3 { size_code.into() } else { 4 }; + /* Consume header byte. */ + pos += 1; + + /* Extract the item value coded on size bytes. */ + if pos + size > desc_len { + return false; + } + value = 0; + for i in 0..size { + value |= (desc[pos + i] as u32) << (8 * i); + } + /* Consume value bytes. */ + pos += size; + + if type_ == HID_RPTDESC_TYPE_GLOBAL && tag == HID_RPTDESC_TAG_USAGE_PAGE { + /* A usage page is a 16 bit value coded on at most 16 bits. */ + if size > 2 { + return false; + } + /* A usage page sets the upper 16 bits of a following usage. */ + usage = (value & 0x0000ffff_u32) << 16; + } + + if type_ == HID_RPTDESC_TYPE_LOCAL && tag == HID_RPTDESC_TAG_USAGE { + /* A usage is a 32 bit value, but is prepended with the current usage page if + * coded on less than 4 bytes (that is, at most 2 bytes). */ + if size == 4 { + usage = value; + } else { + usage = (usage & 0xffff0000_u32) | (value & 0x0000ffff_u32); + } + if usage == FIDO_FULL_USAGE_CTAPHID { + return true; + } + } + } + true +} diff --git a/exts/devmaster/src/bin/tools/mtd_probe/main.rs b/exts/devmaster/src/bin/tools/mtd_probe/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..52c426fe16bcff37afc8eed0e66bb4e6a2a1b085 --- /dev/null +++ b/exts/devmaster/src/bin/tools/mtd_probe/main.rs @@ -0,0 +1,132 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// + +//! +use std::{ + fs::{File, OpenOptions}, + os::unix::prelude::{AsRawFd, FileExt, OpenOptionsExt}, +}; + +use basic::IN_SET; +use log::logger; + +type U8 = ::std::os::raw::c_uchar; +type U32 = ::std::os::raw::c_uint; +type U64 = ::std::os::raw::c_ulonglong; +const SM_SECTOR_SIZE: usize = 512; +const SM_SMALL_PAGE: usize = 256; +const MTD_NANDFLASH: u32 = 4; +const CIS_SIGNATURE: [u8; 10] = [0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20]; + +/// struct mtd_info_user +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct mtd_info_user { + type_: U8, + flags: U32, + size: U32, + erasesize: U32, + writesize: U32, + oobsize: U32, + padding: U64, +} + +nix::ioctl_write_ptr!( + /// ioctl memgetinfo + memgetinfo, + 'M', + 1, + mtd_info_user +); + +fn main() { + logger::init_log_to_console("mtd_probe", log::Level::Info); + let args: Vec = std::env::args().collect(); + + if args.len() != 2 { + println!("usage: mtd_probe /dev/mtd[n]"); + std::process::exit(1); + } + + let mut file = OpenOptions::new() + .custom_flags(libc::O_RDONLY | libc::O_CLOEXEC | libc::O_NOCTTY) + .read(true) + .open(&args[1]) + .unwrap_or_else(|err| { + log::error!("Failed to open {}: {}", args[1], err); + std::process::exit(1); + }); + + let meminfo: mtd_info_user = mtd_info_user::default(); + unsafe { + memgetinfo(file.as_raw_fd(), &meminfo as *const mtd_info_user).unwrap_or_else(|err| { + log::error!("Failed to issue MEMGETINFO ioctl: {}", err); + std::process::exit(1); + }) + }; + std::process::exit(probe_smart_media(&mut file, &meminfo)); +} + +fn probe_smart_media(file: &mut File, meminfo: &mtd_info_user) -> i32 { + let mut cis_buffer = [0_u8; SM_SECTOR_SIZE]; + + if u32::from(meminfo.type_) != MTD_NANDFLASH { + log::error!("Not marked MTD_NANDFLASH."); + return libc::EINVAL; + } + + let sector_size: u64 = meminfo.writesize.into(); + let block_size: u64 = meminfo.erasesize.into(); + let size_in_megs: u64 = (meminfo.size / (1024 * 1024)).into(); + + if IN_SET!(sector_size as usize, SM_SECTOR_SIZE, SM_SMALL_PAGE) { + log::error!("Unexpected sector size: {}", sector_size); + return libc::EINVAL; + } + let spare_count = match size_in_megs { + 1 | 2 => 6, + 4 => 12, + _ => 24, + }; + + let mut off_set = 0_u64; + let mut cis_found = false; + while off_set < block_size * spare_count { + if match file.read_at(&mut cis_buffer, off_set) { + Ok(size) => size, + Err(err) => { + log::error!("Failed to read at: {}", err); + return libc::EINVAL; + } + } == SM_SECTOR_SIZE + { + cis_found = true; + break; + } + off_set += sector_size; + } + + if !cis_found { + log::error!("CIS not found"); + return libc::EINVAL; + } + + if cis_buffer[..CIS_SIGNATURE.len()] != CIS_SIGNATURE + && cis_buffer[SM_SMALL_PAGE..=CIS_SIGNATURE.len()] != CIS_SIGNATURE + { + log::error!("CIS signature didn't match"); + return libc::EINVAL; + } + println!("MTD_FTL=smartmedia"); + 0 +} diff --git a/exts/devmaster/src/bin/tools/v4l_id/main.rs b/exts/devmaster/src/bin/tools/v4l_id/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..0608b9fa83172b2eb4edd38b7d1e14c337b7852c --- /dev/null +++ b/exts/devmaster/src/bin/tools/v4l_id/main.rs @@ -0,0 +1,105 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! v4l_id + +mod videodev2; +use clap::{builder::NonEmptyStringValueParser, Arg, Command}; +use log::logger; +use std::{fs::File, os::unix::prelude::AsRawFd, process}; + +nix::ioctl_write_ptr!( + /// ioctl vidioc_querycap + vidioc_querycap, + 'V', + 0, + videodev2::v4l2_capability +); +const DEVICE_FILE: &str = "device file"; + +fn main() { + logger::init_log_to_console("v4l_id", log::Level::Info); + let arg_matches = Command::new("v4l_id") + .arg( + Arg::new("help") + .long("help") + .short('h') + .help("Video4Linux device identification."), + ) + .arg(Arg::new(DEVICE_FILE).value_parser(NonEmptyStringValueParser::new())) + .get_matches(); + let device: &String = match arg_matches.get_one(DEVICE_FILE) { + Some(device) => device, + None => process::exit(2), + }; + + let device_file = File::open(device).unwrap_or_else(|err| { + log::error!("Failed to open {}: {}", device, err); + process::exit(3); + }); + + let v2cap = videodev2::v4l2_capability { + driver: [0; 16], + card: [0; 32], + bus_info: [0; 32], + version: 0, + capabilities: 0, + device_caps: 0, + reserved: [0; 3], + }; + let res = unsafe { + vidioc_querycap( + device_file.as_raw_fd(), + &v2cap as *const videodev2::v4l2_capability, + ) + } + .unwrap_or_else(|err| { + log::error!("Failed to call vidioc_querycap: {}", err); + process::exit(4); + }); + + if res == 0 { + #[allow(unused_assignments)] + let mut capabilities = 0_u32; + println!("ID_V4L_VERSION=2"); + println!("ID_V4L_PRODUCT={:?}", v2cap.card); + print!("ID_V4L_CAPABILITIES=:"); + if v2cap.capabilities & videodev2::V4L2_CAP_DEVICE_CAPS != 0 { + capabilities = v2cap.device_caps; + } else { + capabilities = v2cap.capabilities; + } + if (capabilities & videodev2::V4L2_CAP_VIDEO_CAPTURE) > 0 + || (capabilities & videodev2::V4L2_CAP_VIDEO_CAPTURE_MPLANE) > 0 + { + print!("capture:"); + } + if (capabilities & videodev2::V4L2_CAP_VIDEO_OUTPUT) > 0 + || (capabilities & videodev2::V4L2_CAP_VIDEO_OUTPUT_MPLANE) > 0 + { + print!("video_output:"); + } + if (capabilities & videodev2::V4L2_CAP_VIDEO_OVERLAY) > 0 { + print!("video_overlay:"); + } + if (capabilities & videodev2::V4L2_CAP_AUDIO) > 0 { + print!("audio:"); + } + if (capabilities & videodev2::V4L2_CAP_TUNER) > 0 { + print!("tuner:"); + } + if (capabilities & videodev2::V4L2_CAP_RADIO) > 0 { + print!("radio:"); + } + println!(); + } +} diff --git a/exts/devmaster/src/bin/tools/v4l_id/videodev2.rs b/exts/devmaster/src/bin/tools/v4l_id/videodev2.rs new file mode 100644 index 0000000000000000000000000000000000000000..8a0935d715e2bcc91ff5902ab90332434a993e69 --- /dev/null +++ b/exts/devmaster/src/bin/tools/v4l_id/videodev2.rs @@ -0,0 +1,38 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +type U8 = ::std::os::raw::c_uchar; +type U32 = ::std::os::raw::c_uint; + +/// struct v4l2_capability +#[doc = " struct v4l2_capability - Describes V4L2 device caps returned by VIDIOC_QUERYCAP\n\n @driver:\t name of the driver module (e.g. \"bttv\")\n @card:\t name of the card (e.g. \"Hauppauge WinTV\")\n @bus_info:\t name of the bus (e.g. \"PCI:\" + pci_name(pci_dev) )\n @version:\t KERNEL_VERSION\n @capabilities: capabilities of the physical device as a whole\n @device_caps: capabilities accessed via this particular device (node)\n @reserved:\t reserved fields for future extensions"] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct v4l2_capability { + pub(super) driver: [U8; 16usize], + pub(super) card: [U8; 32usize], + pub(super) bus_info: [U8; 32usize], + pub(super) version: U32, + pub(super) capabilities: U32, + pub(super) device_caps: U32, + pub(super) reserved: [U32; 3usize], +} + +pub(super) const V4L2_CAP_VIDEO_CAPTURE: u32 = 0x00000001; +pub(super) const V4L2_CAP_VIDEO_OUTPUT: u32 = 0x00000002; +pub(super) const V4L2_CAP_VIDEO_OVERLAY: u32 = 0x00000004; +pub(super) const V4L2_CAP_VIDEO_CAPTURE_MPLANE: u32 = 0x00001000; +pub(super) const V4L2_CAP_VIDEO_OUTPUT_MPLANE: u32 = 0x00002000; +pub(super) const V4L2_CAP_TUNER: u32 = 0x00010000; +pub(super) const V4L2_CAP_AUDIO: u32 = 0x00020000; +pub(super) const V4L2_CAP_RADIO: u32 = 0x00040000; +pub(super) const V4L2_CAP_DEVICE_CAPS: u32 = 0x80000000;