blob: 776df5d63e9b74ce80731121bb6468e13018ece9 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![deny(warnings)]
use std::fs::{File, OpenOptions};
use std::io::{Error, ErrorKind, Read, Seek, SeekFrom, Write};
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use structopt::StructOpt;
const PCI_DIR: &str = "/dev/disk/by-path";
#[cfg(target_arch = "aarch64")]
const PCI_DEVICE: &str = "platform-808100000.pci-pci-0000";
#[cfg(target_arch = "x86_64")]
const PCI_DEVICE: &str = "virtio-pci-0000";
// Ioctl requests are comprised of 32 bits:
// [31:30] Access mode
// [29:16] Size of the parameter structure
// [15:8] Request type
// [7:0] Request number
// For the ioctls we care about here there are no parameters, so we set only a type and a number.
// See Linux:include/uapi/asm-generic/ioctl.h for more details.
const TYPE_SHIFT: usize = 8;
macro_rules! define_ioctl {
($name:ident, $typ:expr, $num:expr, $return_type:ty) => {
fn $name(file: &File) -> $return_type {
let request = ($typ << TYPE_SHIFT) | ($num & 0xff);
let mut r: $return_type = 0;
unsafe {
libc::ioctl(file.as_raw_fd(), request, &mut r);
}
r
}
};
}
// Block ioctl types and number are defined in Linux:include/uapi/linux/fs.h.
define_ioctl!(block_dev_size, 0x12, 96, u64);
define_ioctl!(block_dev_sector_size, 0x12, 104, u32);
#[derive(StructOpt, Debug)]
struct Config {
block_size: u32,
pci_bus: u8,
pci_device: u8,
#[structopt(subcommand)]
cmd: Command,
}
#[derive(StructOpt, Debug)]
enum Command {
#[structopt(name = "check")]
Check { block_count: u64 },
#[structopt(name = "read")]
Read { offset: u64, expected: u8 },
#[structopt(name = "write")]
Write { offset: u64, value: u8 },
}
fn open_block_device(pci_bus: u8, pci_device: u8, write: bool) -> std::io::Result<File> {
let mut path = PathBuf::from(PCI_DIR);
path.push(format!("{}:{:02}:{:02}.0", PCI_DEVICE, pci_bus, pci_device));
OpenOptions::new().read(true).write(write).open(path)
}
fn check(block_dev: &File, block_size: u32, block_count: u64) -> std::io::Result<()> {
let actual_block_size = block_dev_sector_size(block_dev);
let actual_block_count = block_dev_size(block_dev);
if block_size != actual_block_size {
return Err(Error::new(
ErrorKind::Other,
format!("Incorrect size: {} (expected {}).", actual_block_size, block_size),
));
}
if block_count != actual_block_count {
return Err(Error::new(
ErrorKind::Other,
format!("Incorrect count: {} (expected {}).", actual_block_count, block_count),
));
}
Ok(())
}
fn read_block(
block_dev: &mut File,
block_size: u32,
offset: u64,
expected: u8,
) -> std::io::Result<()> {
block_dev.seek(SeekFrom::Start(offset * u64::from(block_size)))?;
let mut data: Vec<u8> = vec![0; block_size as usize];
block_dev.read_exact(&mut data)?;
if !data.iter().all(|&b| b == expected) {
return Err(Error::new(ErrorKind::Other, "Incorrect data read"));
}
Ok(())
}
fn write_block(
block_dev: &mut File,
block_size: u32,
offset: u64,
value: u8,
) -> std::io::Result<()> {
block_dev.seek(SeekFrom::Start(offset * u64::from(block_size)))?;
let data: Vec<u8> = vec![value; block_size as usize];
block_dev.write_all(&data)?;
block_dev.sync_all()?;
Ok(())
}
fn main() -> std::io::Result<()> {
let config = Config::from_args();
let write = if let Command::Write { .. } = config.cmd { true } else { false };
let mut block_dev = open_block_device(config.pci_bus, config.pci_device, write)?;
let result = match config.cmd {
Command::Check { block_count } => check(&block_dev, config.block_size, block_count),
Command::Read { offset, expected } => {
read_block(&mut block_dev, config.block_size, offset, expected)
}
Command::Write { offset, value } => {
write_block(&mut block_dev, config.block_size, offset, value)
}
};
if result.is_ok() {
println!("PASS");
}
result
}