Moved leechcore logic to a seperate struct to facility mocking / unit testing
parent
edaca4ba57
commit
5161467e4d
@ -1 +1 @@
|
|||||||
Subproject commit b48962e97e47666ef0f8d3f6236f7756dd628a12
|
Subproject commit c7c2897b1096f38d6beffa09693c923bb3c5899e
|
@ -0,0 +1,235 @@
|
|||||||
|
use ::std::ptr::null_mut;
|
||||||
|
use std::ffi::c_void;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use memflow::prelude::v1::*;
|
||||||
|
|
||||||
|
use leechcore_sys::*;
|
||||||
|
|
||||||
|
pub const PAGE_SIZE: usize = 0x1000usize;
|
||||||
|
|
||||||
|
pub struct MemReadScatter {
|
||||||
|
pub address: umem,
|
||||||
|
//pub buffer: CSliceMut<'a, u8>,
|
||||||
|
pub buffer: *mut u8,
|
||||||
|
pub buffer_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MemWriteScatter {
|
||||||
|
pub address: umem,
|
||||||
|
//pub buffer: CSliceRef<'a, u8>,
|
||||||
|
pub buffer: *const u8,
|
||||||
|
pub buffer_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait LeechCore {
|
||||||
|
fn volatile(&self) -> bool;
|
||||||
|
fn pa_max(&self) -> u64;
|
||||||
|
|
||||||
|
fn read_scatter(&mut self, mems: &[MemReadScatter]) -> Result<()>;
|
||||||
|
fn write_scatter(&mut self, mems: &[MemWriteScatter]) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LeechCoreSys {
|
||||||
|
handle: Arc<Mutex<HANDLE>>,
|
||||||
|
conf: LC_CONFIG,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LeechCoreSys {
|
||||||
|
pub fn new(
|
||||||
|
device: &str,
|
||||||
|
remote: Option<&str>,
|
||||||
|
with_mem_map: bool,
|
||||||
|
auto_clear: bool,
|
||||||
|
) -> Result<Self> {
|
||||||
|
// open device
|
||||||
|
let mut conf = Self::build_lc_config(device, remote, with_mem_map);
|
||||||
|
let err = std::ptr::null_mut::<PLC_CONFIG_ERRORINFO>();
|
||||||
|
let handle = unsafe { LcCreateEx(&mut conf, err) };
|
||||||
|
if handle.is_null() {
|
||||||
|
// TODO: handle version error
|
||||||
|
// TODO: handle special case of fUserInputRequest
|
||||||
|
return Err(Error(ErrorOrigin::Connector, ErrorKind::Configuration)
|
||||||
|
.log_error(format!("unable to create leechcore context: {err:?}")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: allow handling these errors properly
|
||||||
|
/*
|
||||||
|
typedef struct tdLC_CONFIG_ERRORINFO {
|
||||||
|
DWORD dwVersion; // must equal LC_CONFIG_ERRORINFO_VERSION
|
||||||
|
DWORD cbStruct;
|
||||||
|
DWORD _FutureUse[16];
|
||||||
|
BOOL fUserInputRequest;
|
||||||
|
DWORD cwszUserText;
|
||||||
|
WCHAR wszUserText[];
|
||||||
|
} LC_CONFIG_ERRORINFO, *PLC_CONFIG_ERRORINFO, **PPLC_CONFIG_ERRORINFO;
|
||||||
|
*/
|
||||||
|
|
||||||
|
if auto_clear {
|
||||||
|
let (mut id, mut version_major, mut version_minor) = (0, 0, 0);
|
||||||
|
unsafe {
|
||||||
|
LcGetOption(handle, LC_OPT_FPGA_FPGA_ID, &mut id);
|
||||||
|
LcGetOption(handle, LC_OPT_FPGA_VERSION_MAJOR, &mut version_major);
|
||||||
|
LcGetOption(handle, LC_OPT_FPGA_VERSION_MINOR, &mut version_minor);
|
||||||
|
}
|
||||||
|
if version_major >= 4 && (version_major >= 5 || version_minor >= 7) {
|
||||||
|
// enable auto-clear of status register [master abort].
|
||||||
|
info!("Trying to enable status register auto-clear");
|
||||||
|
let mut data = [0x10, 0x00, 0x10, 0x00];
|
||||||
|
if unsafe {
|
||||||
|
LcCommand(
|
||||||
|
handle,
|
||||||
|
LC_CMD_FPGA_CFGREGPCIE_MARKWR | 0x002,
|
||||||
|
data.len() as u32,
|
||||||
|
data.as_mut_ptr(),
|
||||||
|
null_mut(),
|
||||||
|
null_mut(),
|
||||||
|
)
|
||||||
|
} != 0
|
||||||
|
{
|
||||||
|
info!("Successfully enabled status register auto-clear");
|
||||||
|
} else {
|
||||||
|
return Err(Error(ErrorOrigin::Connector, ErrorKind::Configuration)
|
||||||
|
.log_error("Could not enable status register auto-clear due to outdated bitstream."));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error(ErrorOrigin::Connector, ErrorKind::Configuration)
|
||||||
|
.log_error("Could not enable status register auto-clear due to outdated bitstream. Auto-clear is only available for bitstreams 4.7 and newer."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
handle: Arc::new(Mutex::new(handle)),
|
||||||
|
conf,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_lc_config(device: &str, remote: Option<&str>, with_mem_map: bool) -> LC_CONFIG {
|
||||||
|
// TODO: refactor how the static strings are handled
|
||||||
|
let cdevice = unsafe { &*(device.as_bytes() as *const [u8] as *const [c_char]) };
|
||||||
|
let mut adevice: [c_char; 260] = [0; 260];
|
||||||
|
adevice[..device.len().min(260)].copy_from_slice(&cdevice[..device.len().min(260)]);
|
||||||
|
|
||||||
|
// set remote in case user specified the remote flag
|
||||||
|
let mut aremote: [c_char; 260] = [0; 260];
|
||||||
|
if let Some(remote) = remote {
|
||||||
|
let cremote = unsafe { &*(remote.as_bytes() as *const [u8] as *const [c_char]) };
|
||||||
|
aremote[..remote.len().min(260)].copy_from_slice(&cremote[..remote.len().min(260)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set paMax to -1 if mem map is set to disable automatic scanning
|
||||||
|
let pa_max = if with_mem_map { u64::MAX } else { 0 };
|
||||||
|
|
||||||
|
LC_CONFIG {
|
||||||
|
dwVersion: LC_CONFIG_VERSION,
|
||||||
|
dwPrintfVerbosity: LC_CONFIG_PRINTF_ENABLED | LC_CONFIG_PRINTF_V | LC_CONFIG_PRINTF_VV,
|
||||||
|
szDevice: adevice,
|
||||||
|
szRemote: aremote,
|
||||||
|
pfn_printf_opt: None, // TODO: custom info() wrapper
|
||||||
|
paMax: pa_max,
|
||||||
|
|
||||||
|
// these are set by leechcore so we dont touch them
|
||||||
|
fVolatile: 0,
|
||||||
|
fWritable: 0,
|
||||||
|
fRemote: 0,
|
||||||
|
fRemoteDisableCompress: 0,
|
||||||
|
szDeviceName: [0; 260],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn alloc_scatter2(num_pages: u64) -> Result<PPMEM_SCATTER> {
|
||||||
|
let mut mems = std::ptr::null_mut::<PMEM_SCATTER>();
|
||||||
|
let result = LcAllocScatter2(
|
||||||
|
(num_pages * PAGE_SIZE as u64) as u32,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
num_pages as u32,
|
||||||
|
&mut mems as *mut PPMEM_SCATTER,
|
||||||
|
);
|
||||||
|
if result != 1 {
|
||||||
|
return Err(Error(ErrorOrigin::Connector, ErrorKind::InvalidMemorySize)
|
||||||
|
.log_error("unable to allocate scatter buffer"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(mems)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn mem_free(mems: PPMEM_SCATTER) {
|
||||||
|
LcMemFree(mems as *mut c_void);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LeechCore for LeechCoreSys {
|
||||||
|
fn volatile(&self) -> bool {
|
||||||
|
self.conf.fVolatile != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pa_max(&self) -> u64 {
|
||||||
|
self.conf.paMax
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_scatter(&mut self, mems: &[MemReadScatter]) -> Result<()> {
|
||||||
|
let num_pages = mems.len() as u64;
|
||||||
|
|
||||||
|
// allocate read buffers
|
||||||
|
let lc_mems = unsafe { Self::alloc_scatter2(num_pages)? };
|
||||||
|
|
||||||
|
// copy over memory definitions
|
||||||
|
for (i, mem) in mems.iter().enumerate() {
|
||||||
|
let lc_mem = unsafe { *lc_mems.add(i) };
|
||||||
|
unsafe { (*lc_mem).qwA = mem.address };
|
||||||
|
unsafe { (*lc_mem).__bindgen_anon_1.pb = mem.buffer };
|
||||||
|
unsafe { (*lc_mem).cb = mem.buffer_len as u32 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// dispatch read
|
||||||
|
{
|
||||||
|
let handle = self.handle.lock().unwrap();
|
||||||
|
unsafe {
|
||||||
|
LcReadScatter(*handle, num_pages as u32, lc_mems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// free buffers
|
||||||
|
unsafe {
|
||||||
|
Self::mem_free(lc_mems);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_scatter(&mut self, mems: &[MemWriteScatter]) -> Result<()> {
|
||||||
|
let num_pages = mems.len() as u64;
|
||||||
|
|
||||||
|
// allocate write buffers
|
||||||
|
let lc_mems = unsafe { Self::alloc_scatter2(num_pages)? };
|
||||||
|
|
||||||
|
// copy over memory definitions
|
||||||
|
for (i, mem) in mems.iter().enumerate() {
|
||||||
|
let lc_mem = unsafe { *lc_mems.add(i) };
|
||||||
|
unsafe { (*lc_mem).qwA = mem.address };
|
||||||
|
unsafe { (*lc_mem).__bindgen_anon_1.pb = mem.buffer as *mut u8 };
|
||||||
|
unsafe { (*lc_mem).cb = mem.buffer_len as u32 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// dispatch write
|
||||||
|
{
|
||||||
|
let handle = self.handle.lock().unwrap();
|
||||||
|
unsafe {
|
||||||
|
LcWriteScatter(*handle, num_pages as u32, lc_mems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// free buffers
|
||||||
|
unsafe {
|
||||||
|
Self::mem_free(lc_mems);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LeechCoreMock {}
|
Loading…
Reference in New Issue