Moved to seperate project from memflow

rust_native
ko1N 5 years ago
commit 7cfbf8dbe6

5
.gitignore vendored

@ -0,0 +1,5 @@
/target
**/*.rs.bk
*.swp
.vscode
Cargo.lock

@ -0,0 +1,32 @@
[package]
name = "memflow-pcileech"
version = "0.1.0"
authors = ["ko1N <ko1N1337@gmail.com>"]
edition = "2018"
description = "pcileech fpga connector for the memflow physical memory introspection framework"
homepage = "https://github.com/memflow/memflow-pcileech"
repository = "https://github.com/memflow/memflow-pcileech"
readme = "README.md"
[lib]
crate-type = ["lib", "cdylib"]
[dependencies]
memflow-core = { path = "../memflow/memflow-core" }
memflow-derive = { path = "../memflow/memflow-derive" }
log = { version = "0.4.8", default-features = false }
rusb = "0.6.0"
dataview = "0.1"
bitfield = "0.13.2"
[dev-dependencies]
clap = "2.33.0"
simple_logger = "1.0.1"
[features]
default = []
plugin = []
[[example]]
name = "read_pcileech"
path = "examples/read_pcileech.rs"

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 ko1N
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,9 @@
use log::Level;
use memflow_core::connector::ConnectorArgs;
use memflow_pcileech::create_connector;
fn main() {
simple_logger::init_with_level(Level::Trace).unwrap();
create_connector(&ConnectorArgs::new()).unwrap();
}

@ -0,0 +1,486 @@
use crate::ft60x::*;
use std::mem::MaybeUninit;
use std::time::Duration;
use log::{info, trace, warn};
use memflow_core::{
error::{Error, Result},
size,
};
use bitfield::bitfield;
use dataview::Pod;
pub const FPGA_CONFIG_CORE: u16 = 0x0003;
pub const FPGA_CONFIG_PCIE: u16 = 0x0001;
pub const FPGA_CONFIG_SPACE_READONLY: u16 = 0x0000;
pub const FPGA_CONFIG_SPACE_READWRITE: u16 = 0x8000;
// TODO: remove unused
#[allow(unused)]
pub struct PhyConfig {
magic: u8, // 8 bit
tp_cfg: u8, // 4 bit
tp: u8, // 4 bit
pub wr: PhyConfigWr, // 16 bits
pub rd: PhyConfigRd, // 32 bits
}
bitfield! {
pub struct PhyConfigWr(u16);
impl Debug;
pl_directed_link_auton, _: 0;
pl_directed_link_change, _: 2, 1;
pl_directed_link_speed, _: 3;
pl_directed_link_width, _: 5, 4;
pl_upstream_prefer_deemph, _: 6;
pl_transmit_hot_rst, _: 7;
pl_downstream_deemph_source, _: 8;
//_, _: 16, 9;
}
bitfield! {
pub struct PhyConfigRd(u32);
impl Debug;
pl_ltssm_state, _: 5, 0;
pl_rx_pm_state, _: 7, 6;
pl_tx_pm_state, _: 10, 8;
pl_initial_link_width, _: 13, 11;
pl_lane_reversal_mode, _: 15, 14;
pl_sel_lnk_width, _: 17, 16;
pl_phy_lnk_up, _: 18;
pl_link_gen2_cap, _: 19;
pl_link_partner_gen2_supported, _: 20;
pl_link_upcfg_cap, _: 21;
pl_sel_lnk_rate, _: 22;
pl_directed_change_done, _: 23;
pl_received_hot_rst, _: 24;
//_, _: 31, 25;
}
pub struct Device {
ft60: FT60x,
}
impl Device {
pub fn new() -> Result<Self> {
let mut ft60 = FT60x::new()?;
ft60.abort_pipe(0x02)?;
ft60.abort_pipe(0x82)?;
ft60.set_suspend_timeout(Duration::new(0, 0))?;
// check chip configuration
let mut conf = ft60.config()?;
trace!(
"ft60x config: fifo_mode={} channel_config={} optional_feature={}",
conf.fifo_mode,
conf.channel_config,
conf.optional_feature_support
);
if conf.fifo_mode != FifoMode::Mode245 as i8
|| conf.channel_config != ChannelConfig::Config1 as i8
|| conf.optional_feature_support != OptionalFeatureSupport::DisableAll as i16
{
warn!("bad ft60x config, reconfiguring chip");
conf.fifo_mode = FifoMode::Mode245 as i8;
conf.channel_config = ChannelConfig::Config1 as i8;
conf.optional_feature_support = OptionalFeatureSupport::DisableAll as i16;
ft60.set_config(&conf)?;
} else {
info!("ft60x config is valid");
}
Ok(Self { ft60 })
}
pub fn get_version(&mut self) -> Result<()> {
// DeviceFPGA_GetDeviceId_FpgaVersion_ClearPipe
self.read_version_clear_pipe()?;
self.read_version_v4()?;
Ok(())
}
fn read_version_clear_pipe(&mut self) -> Result<()> {
let dummy = [
// cmd msg: FPGA bitstream version (major.minor) v4
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x13, 0x77,
// cmd msg: FPGA bitstream version (major) v3
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x77,
];
self.ft60.write_pipe(&dummy)?;
let mut buf = vec![0u8; size::mb(16)];
let bytes = self.ft60.read_pipe(&mut buf[..0x1000])?;
if bytes >= 0x1000 {
self.ft60.read_pipe(&mut buf)?;
}
Ok(())
}
fn read_version_v4(&mut self) -> Result<()> {
let version_major =
self.read_config::<u8>(0x0008, FPGA_CONFIG_CORE | FPGA_CONFIG_SPACE_READONLY)?;
println!("version_major = {}", version_major);
let version_minor =
self.read_config::<u8>(0x0009, FPGA_CONFIG_CORE | FPGA_CONFIG_SPACE_READONLY)?;
println!("version_minor = {}", version_minor);
let fpga_id =
self.read_config::<u8>(0x000a, FPGA_CONFIG_CORE | FPGA_CONFIG_SPACE_READONLY)?;
println!("fpga_id = {}", fpga_id);
// this will cause the hardware to reset briefly
/*
let inactivity_timer = 0x000186a0u32; // set inactivity timer to 1ms (0x0186a0 * 100MHz) [only later activated on UDP bitstreams]
self.write_config(
0x0008,
inactivity_timer,
FPGA_CONFIG_CORE | FPGA_CONFIG_SPACE_READWRITE,
)?;
*/
let mut device_id = self
.read_config::<u16>(0x0008, FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READONLY)
.unwrap_or_default();
if device_id == 0 {
let magic_pcie = self
.read_config::<u16>(0x0000, FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READWRITE)
.unwrap_or_default();
println!("magic_pcie = {:?}", magic_pcie);
if magic_pcie == 0x6745 {
println!("failed to get device_id - trying to recover via hot reset");
self.hot_reset_v4().ok();
device_id = self
.read_config::<u16>(0x0008, FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READONLY)
.unwrap_or_default();
}
}
println!("device_id = {:?}", device_id);
let (wr, rd) = self.get_phy_v4()?;
println!("wr: {:?}", wr);
println!("rd: {:?}", rd);
/*
ctx->wDeviceId = _byteswap_ushort(wbsDeviceId);
ctx->phySupported = DeviceFPGA_GetPHYv4(ctx);
*/
Ok(())
}
fn hot_reset_v4(&mut self) -> Result<()> {
trace!("hot resetting the fpga");
let (wr, _) = self.get_phy_v4()?;
self.write_config(0x0016, wr.0, FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READWRITE)?;
std::thread::sleep(Duration::from_millis(250)); // TODO: poll pl_ltssm_state + timeout with failure
self.write_config(0x0016, wr.0, FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READWRITE)?;
Ok(())
}
fn get_phy_v4(&mut self) -> Result<(PhyConfigWr, PhyConfigRd)> {
let wr_raw =
self.read_config::<u16>(0x0016, FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READWRITE)?;
let rd_raw =
self.read_config::<u32>(0x000a, FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READONLY)?;
Ok((PhyConfigWr { 0: wr_raw }, PhyConfigRd { 0: rd_raw }))
}
#[allow(clippy::uninit_assumed_init)]
fn read_config<T: Pod>(&mut self, addr: u16, flags: u16) -> Result<T> {
let mut obj: T = unsafe { MaybeUninit::uninit().assume_init() };
self.read_config_into_raw(addr, obj.as_bytes_mut(), flags)?;
Ok(obj)
}
fn read_config_build_request(addr: u16, bytes: u16, flags: u16) -> Vec<u8> {
let mut res = Vec::new();
for a in (addr..addr + bytes).step_by(2) {
let mut req = [0u8; 8];
req[4] = ((a | (flags & 0x8000)) >> 8) as u8;
req[5] = (a & 0xff) as u8;
req[6] = (0x10 | (flags & 0x03)) as u8;
req[7] = 0x77;
res.extend_from_slice(&req);
}
res
}
fn read_config_parse_response(
addr: u16,
respbuf: &[u8],
outbuf: &mut [u8],
flags: u16,
) -> Result<()> {
let view = respbuf.as_data_view();
let mut skip = 0;
for i in (0..respbuf.len()).step_by(32) {
if i + skip >= respbuf.len() {
break;
}
while view.copy::<u32>(i + skip) == 0x55556666 {
//trace!("ftdi workaround detected, skipping 4 bytes");
skip += 4;
if i + skip + 32 > respbuf.len() {
return Err(Error::Connector("out of range config read"));
}
}
let mut status = view.copy::<u32>(i + skip);
if status & 0xf0000000 != 0xe0000000 {
trace!("invalid status reply, skipping");
}
trace!("parsing data buffer");
for j in 0..7 {
let status_flag = (status & 0x0f) == (flags & 0x03) as u32;
status >>= 4; // move to next status
if !status_flag {
//trace!("status source flag does not match source");
continue;
}
let data = view.copy::<u32>(i + skip + 4 + j * 4);
let mut a = (data as u16).to_be(); // only enforce a byteswap if we are on le
a -= (flags & 0x8000) + addr;
if a >= outbuf.len() as u16 {
trace!("address data out of range, skipping");
continue;
}
if a == outbuf.len() as u16 - 1 {
outbuf[a as usize] = ((data >> 16) & 0xff) as u8;
} else {
let b = (((data >> 16) & 0xffff) as u16).to_le_bytes();
outbuf[a as usize] = b[0];
outbuf[a as usize + 1] = b[1];
}
}
}
Ok(())
}
fn read_config_into_raw(&mut self, addr: u16, buf: &mut [u8], flags: u16) -> Result<()> {
if buf.is_empty() || buf.len() > size::kb(4) || addr > size::kb(4) as u16 {
return Err(Error::Connector("invalid config address requested"));
}
let req = Self::read_config_build_request(addr, buf.len() as u16, flags);
self.ft60.write_pipe(&req)?;
let mut readbuf = [0u8; size::kb(128)];
let bytes = self.ft60.read_pipe(&mut readbuf)?;
Self::read_config_parse_response(addr, &readbuf[..bytes], buf, flags)
}
fn write_config<T: Pod>(&mut self, addr: u16, obj: T, flags: u16) -> Result<()> {
self.write_config_raw(addr, obj.as_bytes(), flags)
}
fn write_config_raw(&mut self, addr: u16, buf: &[u8], flags: u16) -> Result<()> {
if buf.is_empty() || buf.len() > 0x200 || addr > size::kb(4) as u16 {
return Err(Error::Connector("invalid config address to write"));
}
let mut outbuf = [0u8; 0x800];
let mut ptr = 0;
for i in (0..buf.len()).step_by(2) {
let a = (addr + i as u16) | (flags & 0x8000);
outbuf[ptr] = buf[i as usize]; // byte_value_addr
outbuf[ptr + 1] = if buf.len() == i + 1 {
0
} else {
buf[i as usize + 1]
}; // byte_value_addr + 1
outbuf[ptr + 2] = 0xFF; // byte_mask_addr
outbuf[ptr + 3] = if buf.len() == i + 1 { 0 } else { 0xFF }; // byte_mask_addr + 1
outbuf[ptr + 4] = (a >> 8) as u8; // addr_high = bit[6:0], write_regbank = bit[7]
outbuf[ptr + 5] = (a & 0xFF) as u8; // addr_low
outbuf[ptr + 6] = (0x20 | (flags & 0x03)) as u8; // target = bit[0:1], read = bit[4], write = bit[5]
outbuf[ptr + 7] = 0x77; // MAGIC 0x77
ptr += 8;
}
self.ft60.write_pipe(&buf)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem::size_of;
#[test]
fn test_struct_sizes() {
assert_eq!(size_of::<PhyConfigWr>(), 2);
assert_eq!(size_of::<PhyConfigRd>(), 4);
}
#[test]
fn test_config_read_build_request() {
assert_eq!(
Device::read_config_build_request(
0x0008,
1,
FPGA_CONFIG_CORE | FPGA_CONFIG_SPACE_READONLY
),
[0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x13, 0x77]
);
assert_eq!(
Device::read_config_build_request(
0x0009,
1,
FPGA_CONFIG_CORE | FPGA_CONFIG_SPACE_READONLY
),
[0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x13, 0x77]
);
assert_eq!(
Device::read_config_build_request(
0x0008,
2,
FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READONLY
),
[0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x11, 0x77]
);
assert_eq!(
Device::read_config_build_request(
0x0000,
2,
FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READWRITE
),
[0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x11, 0x77]
);
assert_eq!(
Device::read_config_build_request(
0x0016,
2,
FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READWRITE
),
[0x0, 0x0, 0x0, 0x0, 0x80, 0x16, 0x11, 0x77]
);
assert_eq!(
Device::read_config_build_request(
0x000a,
4,
FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READONLY
),
[0x0, 0x0, 0x0, 0x0, 0x0, 0xA, 0x11, 0x77, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC, 0x11, 0x77]
);
}
#[test]
fn test_config_parse_version_major() {
let mut version_major = 0u8;
Device::read_config_parse_response(
0x0008,
&[
102, 102, 85, 85, 102, 102, 85, 85, 102, 102, 85, 85, 102, 102, 85, 85, 102, 102,
85, 85, 243, 255, 255, 239, 0, 8, 4, 2, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
],
version_major.as_bytes_mut(),
FPGA_CONFIG_CORE | FPGA_CONFIG_SPACE_READONLY,
)
.unwrap();
assert_eq!(version_major, 4);
}
#[test]
fn test_config_parse_version_minor() {
let mut version_minor = 0u8;
Device::read_config_parse_response(
0x0009,
&[
102, 102, 85, 85, 102, 102, 85, 85, 102, 102, 85, 85, 102, 102, 85, 85, 102, 102,
85, 85, 243, 255, 255, 239, 0, 9, 2, 1, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
],
version_minor.as_bytes_mut(),
FPGA_CONFIG_CORE | FPGA_CONFIG_SPACE_READONLY,
)
.unwrap();
assert_eq!(version_minor, 2);
}
#[test]
fn test_config_parse_fpga_id() {
let mut fpga_id = 0u8;
Device::read_config_parse_response(
0x000a,
&[
102, 102, 85, 85, 102, 102, 85, 85, 102, 102, 85, 85, 102, 102, 85, 85, 102, 102,
85, 85, 243, 255, 255, 239, 0, 10, 1, 0, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
],
fpga_id.as_bytes_mut(),
FPGA_CONFIG_CORE | FPGA_CONFIG_SPACE_READONLY,
)
.unwrap();
assert_eq!(fpga_id, 1);
}
#[test]
fn test_config_parse_device_id() {
let mut fpga_id = 0u8;
Device::read_config_parse_response(
0x000a,
&[
102, 102, 85, 85, 102, 102, 85, 85, 102, 102, 85, 85, 102, 102, 85, 85, 102, 102,
85, 85, 243, 255, 255, 239, 0, 10, 1, 0, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
],
fpga_id.as_bytes_mut(),
FPGA_CONFIG_CORE | FPGA_CONFIG_SPACE_READONLY,
)
.unwrap();
assert_eq!(fpga_id, 1);
}
#[test]
fn test_config_parse_phy_wr() {
let mut wr = 0u16;
Device::read_config_parse_response(
0x0016,
&[
102, 102, 85, 85, 102, 102, 85, 85, 102, 102, 85, 85, 102, 102, 85, 85, 102, 102,
85, 85, 241, 255, 255, 239, 128, 22, 72, 0, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
],
wr.as_bytes_mut(),
FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READWRITE,
)
.unwrap();
assert_eq!(wr, 0x48);
}
#[test]
fn test_config_parse_phy_rd() {
let mut rd = 0u32;
Device::read_config_parse_response(
0x000a,
&[
102, 102, 85, 85, 102, 102, 85, 85, 102, 102, 85, 85, 102, 102, 85, 85, 102, 102,
85, 85, 17, 255, 255, 239, 0, 10, 25, 8, 0, 12, 28, 0, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
],
rd.as_bytes_mut(),
FPGA_CONFIG_PCIE | FPGA_CONFIG_SPACE_READONLY,
)
.unwrap();
assert_eq!(rd, 0x1C0819);
}
}

@ -0,0 +1,200 @@
/// This module provides a wrapper around libusb to communicate with a FT60x chip directly.
/// The main interface will be through the chip configuration and read/write pipe functions.
mod chip;
pub use chip::{ChannelConfig, FifoMode, OptionalFeatureSupport};
use rusb::{
request_type, DeviceHandle, DeviceList, Direction, GlobalContext, Recipient, RequestType,
};
use std::mem::size_of;
use std::time::Duration;
use log::{info, trace};
use memflow_core::error::{Error, Result};
use dataview::Pod;
pub const FTDI_VENDOR_ID: u16 = 0x0403;
pub const FTDI_FT60X_PRODUCT_ID: u16 = 0x601f;
// TODO: enum?
pub const FTDI_COMMUNICATION_INTERFACE: u8 = 0x00;
pub const FTDI_DATA_INTERFACE: u8 = 0x01;
// TODO: enum?
pub const FTDI_ENDPOINT_SESSION_OUT: u8 = 0x01;
pub const FTDI_ENDPOINT_OUT: u8 = 0x02;
pub const FTDI_ENDPOINT_IN: u8 = 0x82;
pub struct FT60x {
handle: DeviceHandle<GlobalContext>,
}
impl FT60x {
/// Tries to open a usb connection to a FT60x chip connected via libusb.
// TODO: handle multiple devices at the same time
pub fn new() -> Result<Self> {
let (dev, desc) = DeviceList::new()
.map_err(|_| Error::Connector("unable to get usb device list"))?
.iter()
.filter_map(|dev| match dev.device_descriptor() {
Ok(desc) => Some((dev, desc)),
Err(_) => None,
})
.find(|(_dev, desc)| {
desc.vendor_id() == FTDI_VENDOR_ID && desc.product_id() == FTDI_FT60X_PRODUCT_ID
})
.ok_or_else(|| Error::Connector("unable to find ftdi device"))?;
info!(
"found FTDI device: {}:{} (bus {}, device {})",
desc.vendor_id(),
desc.product_id(),
dev.bus_number(),
dev.address()
);
// open handle and reset chip
let mut handle = dev
.open()
.map_err(|_| Error::Connector("unable to open ftdi usb device"))?;
handle
.reset()
.map_err(|_| Error::Connector("unable to reset ftdi device"))?;
/*
let manufacturer = handle
.read_string_descriptor_ascii(desc.manufacturer_string_index().unwrap_or_default())
.map_err(|_| Error::Connector("unable to read ftdi manufacturer name"))?;
let product = handle
.read_string_descriptor_ascii(desc.product_string_index().unwrap_or_default())
.map_err(|_| Error::Connector("unable to read ftdi product name"))?;
let serial = handle
.read_string_descriptor_ascii(desc.serial_number_string_index().unwrap_or_default())
.map_err(|_| Error::Connector("unable to read ftdi serial number"))?;
info!(
"device: manufacturer={} product={} serial={}",
manufacturer, product, serial
);
*/
// check driver state
if handle
.kernel_driver_active(FTDI_COMMUNICATION_INTERFACE)
.map_err(|_| Error::Connector("ftdi driver check failed"))?
{
return Err(Error::Connector(
"ftdi driver is already active on FTDI_COMMUNICATION_INTERFACE",
));
}
info!("ftdi driver is not active on FTDI_COMMUNICATION_INTERFACE");
if handle
.kernel_driver_active(FTDI_DATA_INTERFACE)
.map_err(|_| Error::Connector("ftdi driver check failed"))?
{
return Err(Error::Connector(
"ftdi driver is already active on FTDI_DATA_INTERFACE",
));
}
info!("ftdi driver is not active on FTDI_DATA_INTERFACE");
// claim interfaces
handle
.claim_interface(FTDI_COMMUNICATION_INTERFACE)
.map_err(|_| Error::Connector("unable to claim FTDI_COMMUNICATION_INTERFACE"))?;
handle
.claim_interface(FTDI_DATA_INTERFACE)
.map_err(|_| Error::Connector("unable to claim FTDI_DATA_INTERFACE"))?;
Ok(Self { handle })
}
pub fn abort_pipe(&mut self, pipe_id: u8) -> Result<()> {
// dummy function, only used for ftdi compat
trace!("abort_pipe: {}", pipe_id);
Ok(())
}
pub fn set_suspend_timeout(&mut self, timeout: Duration) -> Result<()> {
// dummy function, only used for ftdi compat
trace!("set_suspend_timeout: {:?}", timeout);
Ok(())
}
/// Retrieves the FT60x chip configuration
pub fn config(&mut self) -> Result<chip::Config> {
let mut buf = vec![0u8; size_of::<chip::Config>()];
self.handle
.read_control(
request_type(Direction::In, RequestType::Vendor, Recipient::Device),
0xCF,
1,
0,
&mut buf,
Duration::from_millis(1000),
)
.map_err(|_| Error::Connector("unable to get ft60x config"))?;
// dataview buf to struct
let view = buf.as_data_view();
Ok(view.copy::<chip::Config>(0))
}
/// Writes the FT60x chip configuration
pub fn set_config(&mut self, conf: &chip::Config) -> Result<()> {
let bytes = self
.handle
.write_control(
request_type(Direction::Out, RequestType::Vendor, Recipient::Device),
0xCF,
0,
0,
conf.as_bytes(),
Duration::from_millis(1000),
)
.map_err(|_| Error::Connector("unable to set ft60x config"))?;
if bytes == size_of::<chip::Config>() {
Ok(())
} else {
Err(Error::Connector("unable to set ft60x config"))
}
}
/// Write to the ft60x chip
pub fn write_pipe(&mut self, data: &[u8]) -> Result<()> {
self.write_bulk_raw(FTDI_ENDPOINT_OUT, data)
}
/// Read from the ft60x chip
pub fn read_pipe(&mut self, data: &mut [u8]) -> Result<usize> {
self.send_read_request(data.len() as u32)?;
self.handle
.read_bulk(FTDI_ENDPOINT_IN, data, Duration::from_millis(1000))
.map_err(|_| Error::Connector("unable to read from ft60x"))
}
/// Sends a ControlRequest to issue a read with a given size
fn send_read_request(&mut self, len: u32) -> Result<()> {
let req = chip::ControlRequest::new(1, FTDI_ENDPOINT_IN, 1, len);
self.write_bulk_raw(FTDI_ENDPOINT_SESSION_OUT, req.as_bytes())
}
/// Does a raw bulk write to the chip and validates the sent size
fn write_bulk_raw(&self, endpoint: u8, buf: &[u8]) -> Result<()> {
// TODO: customizable write_bulk timeout
let bytes = self
.handle
.write_bulk(endpoint, buf, Duration::from_millis(1000))
.map_err(|_| Error::Connector("unable to write to ft60x"))?;
if bytes == buf.len() {
Ok(())
} else {
Err(Error::Connector(
"unable to write the entire buffer to the ft60x",
))
}
}
}

@ -0,0 +1,110 @@
use dataview::Pod;
/// Chip configuration - FIFO Mode
#[allow(unused)]
pub enum FifoMode {
Mode245 = 0,
Mode600 = 1,
Max = 2,
}
/// Chip configuration - Channel Configuration
#[allow(unused)]
pub enum ChannelConfig {
Config4 = 0,
Config2 = 1,
Config1 = 2,
Config1OutPipe = 3,
Config1InPipe = 4,
Max = 5,
}
/// Chip configuration - Optional Feature Support
#[allow(unused)]
pub enum OptionalFeatureSupport {
DisableAll = 0,
EnableBatteryCharging = 1,
DisableCancelSessionUnderrun = 2,
EnableNotificationMessageInch1 = 4,
EnableNotificationMessageInch2 = 8,
EnableNotificationMessageInch3 = 0x10,
EnableNotificationMessageInch4 = 0x20,
EnableNotificationMessageInchAll = 0x3C,
DisableUnderrunInch1 = 0x1 << 6,
DisableUnderrunInch2 = 0x1 << 7,
DisableUnderrunInch3 = 0x1 << 8,
DisableUnderrunInch4 = 0x1 << 9,
DisableUnderrunInchAll = 0xF << 6,
}
/// Chip configuration - Config structure
#[repr(C)]
#[derive(Clone, Pod)]
pub struct Config {
// Device Descriptor
pub vendor_id: i16,
pub product_id: i16,
// String Descriptors
pub string_descriptors: [i8; 128],
// Configuration Descriptor
reserved1: i8,
pub power_attributes: i8,
pub power_consumption: i16,
// Data Transfer Configuration
reserved2: i8,
pub fifo_clock: i8,
pub fifo_mode: i8,
pub channel_config: i8,
// Optional Feature Support
pub optional_feature_support: i16,
pub battery_charging_gpio_config: i8,
pub flash_eeprom_detection: i8, // Read-only
// MSIO and GPIO Configuration
pub msio_control: u32,
pub gpio_control: u32,
}
#[repr(C, packed)]
#[derive(Copy, Clone, Pod)]
pub struct ControlRequest {
pub idx: u32,
pub pipe: u8,
pub cmd: u8,
unknown1: u8,
unknown2: u8,
pub len: u32,
unknown3: u32,
unknown4: u32,
}
impl ControlRequest {
pub fn new(idx: u32, pipe: u8, cmd: u8, len: u32) -> Self {
Self {
idx,
pipe,
cmd,
unknown1: 0,
unknown2: 0,
len,
unknown3: 0,
unknown4: 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem::size_of;
#[test]
fn test_struct_sizes() {
assert_eq!(size_of::<Config>(), 0x98);
assert_eq!(size_of::<ControlRequest>(), 0x14);
}
}

@ -0,0 +1,40 @@
mod fpga;
mod ft60x;
use memflow_core::connector::ConnectorArgs;
use memflow_core::*;
use memflow_derive::connector;
#[allow(unused)]
pub struct PciLeech {
device: fpga::Device,
}
impl PciLeech {
pub fn new() -> Result<Self> {
let mut device = fpga::Device::new()?;
device.get_version()?;
Ok(Self { device })
}
}
impl PhysicalMemory for PciLeech {
fn phys_read_raw_list(&mut self, _data: &mut [PhysicalReadData]) -> Result<()> {
Err(Error::Connector(
"memflow_pcileech::phys_read_iter not implemented",
))
}
fn phys_write_raw_list(&mut self, _data: &[PhysicalWriteData]) -> Result<()> {
Err(Error::Connector(
"memflow_pcileech::phys_write_iter not implemented",
))
}
}
// TODO: handle args properly
/// Creates a new Pcileech Connector instance.
#[connector(name = "pcileech")]
pub fn create_connector(_args: &ConnectorArgs) -> Result<PciLeech> {
PciLeech::new()
}
Loading…
Cancel
Save