252 lines
7.7 KiB
Rust
252 lines
7.7 KiB
Rust
use std::{
|
|
mem::offset_of,
|
|
ops::{Deref, DerefMut, Range},
|
|
pin::Pin,
|
|
sync::{atomic::Ordering, Arc},
|
|
};
|
|
|
|
use bitfield_struct::bitfield;
|
|
use jni::JavaVM;
|
|
use unicorn_engine::Unicorn;
|
|
use zerocopy::{
|
|
big_endian::U16 as U16BE,
|
|
little_endian::{U16, U32, U64},
|
|
FromBytes, Immutable, IntoBytes, KnownLayout,
|
|
};
|
|
|
|
use crate::{
|
|
align_up, component::{Component, ComponentInterface}, core::CpuContext, memory_map::RAM_START, overlapping, range_of_field, unsync_cell::UnsyncCell, unsync_read
|
|
};
|
|
|
|
use super::ComponentDma;
|
|
|
|
pub struct ComponentIoResult {
|
|
component_index: usize,
|
|
direction: ComponentIoDirection,
|
|
}
|
|
|
|
pub enum ComponentIoDirection {
|
|
ToComponent,
|
|
ToGuest,
|
|
}
|
|
|
|
impl Component {
|
|
pub fn perform_write<D>(
|
|
&self,
|
|
range: Range<usize>,
|
|
writer: impl FnOnce(&UnsyncCell<ComponentInterface>),
|
|
ram: Arc<UnsyncCell<[u8]>>,
|
|
java_vm: JavaVM,
|
|
) -> Option<Pin<Box<dyn Future<Output = ComponentIoResult>>>> {
|
|
let Self::Populated {
|
|
read_transfer_active,
|
|
write_transfer_active,
|
|
info,
|
|
interface,
|
|
own_index,
|
|
java_component,
|
|
..
|
|
} = self
|
|
else {
|
|
return None;
|
|
};
|
|
|
|
if overlapping(&range, &range_of_field!(ComponentInterface, info)) {
|
|
return None;
|
|
}
|
|
let read_range = range_of_field!(ComponentInterface, read_dma);
|
|
if overlapping(&range, &read_range) {
|
|
if read_transfer_active.load(Ordering::Acquire) || read_range != range {
|
|
return None;
|
|
}
|
|
|
|
writer(interface);
|
|
|
|
let dma: ComponentDma = unsync_read!(interface, ComponentInterface, read_dma);
|
|
if dma.dma_enabled()
|
|
&& read_transfer_active.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire).is_ok()
|
|
{
|
|
if !overlapping(
|
|
&(dma.address..dma.address + dma.size()),
|
|
&(RAM_START..RAM_START + ram.len() as u64),
|
|
) {
|
|
read_transfer_active.store(false, Ordering::Release);
|
|
return None;
|
|
}
|
|
let bytes_per_chunk: u32 = info.bytes_per_tick / 5;
|
|
let java_component =
|
|
let index = return Some(Box::pin(async move {
|
|
let java_component = java_component;
|
|
ComponentIoResult { component_index: 0, direction: todo!() }
|
|
}));
|
|
}
|
|
|
|
return None;
|
|
}
|
|
// let write_range = range_of_field!(ComponentInterface, write_dma);
|
|
// if write_transfer_active && overlapping(range.clone(), write_range) {
|
|
// return;
|
|
// }
|
|
|
|
return None;
|
|
}
|
|
}
|
|
|
|
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
|
|
#[repr(C)]
|
|
struct ComponentMmioHeader {
|
|
component_count: U32,
|
|
/// version bump when component access method (offset table) changes or [ComponentInfo] is non-additively changed
|
|
version: U16,
|
|
control: U16BE,
|
|
}
|
|
|
|
#[bitfield(u16)]
|
|
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
|
|
pub struct ComponentMmioControl {
|
|
interrupt_added: bool,
|
|
interrupt_removed: bool,
|
|
#[bits(2)]
|
|
_reserved: U16,
|
|
/// read-write, indicates the core to fire interrupts on. indicating an invalid core means no core will receive the interrupt
|
|
#[bits(4)]
|
|
interrupt_firing_core: u8,
|
|
#[bits(8)]
|
|
_reserved: U16,
|
|
}
|
|
|
|
pub fn calculate_components_start(component_count: usize) -> usize {
|
|
OFFSETS_START + align_up(component_count as u64 * 4 + 16, 0x1000) as usize
|
|
}
|
|
|
|
fn read_bytes_to_u64(bytes: &[u8]) -> u64 {
|
|
match bytes.len() {
|
|
1 => bytes[0] as u64,
|
|
2 => U16::read_from_bytes(bytes).unwrap().get() as u64,
|
|
4 => U32::read_from_bytes(bytes).unwrap().get() as u64,
|
|
8 => U64::read_from_bytes(bytes).unwrap().get() as u64,
|
|
16 => todo!("128 bit reads are currently neither implemented here nor supported by unicorn hooks"),
|
|
_ => panic!("expected byte slice of primitive size"),
|
|
}
|
|
}
|
|
|
|
fn write_bytes_from_u64(bytes: &mut [u8], value: u64) {
|
|
match bytes.len() {
|
|
1 => bytes[0] = value.try_into().unwrap(),
|
|
2 => U16::new(value.try_into().unwrap()).write_to(bytes).unwrap(),
|
|
4 => U32::new(value.try_into().unwrap()).write_to(bytes).unwrap(),
|
|
8 => U64::new(value).write_to(bytes).unwrap(),
|
|
16 => todo!("128 bit writes are currently neither implemented here nor supported by unicorn hooks"),
|
|
_ => panic!("expected byte slice of primitive size"),
|
|
}
|
|
}
|
|
|
|
const OFFSETS_START: usize = 0x200;
|
|
const INTERFACE_INTERVAL: usize = 0x800;
|
|
|
|
pub fn component_mmio_read(vcpu: &mut Unicorn<CpuContext>, offset: u64, size: usize) -> u64 {
|
|
let offset = offset as usize;
|
|
let component_count = vcpu.get_data().components.len();
|
|
if offset < OFFSETS_START {
|
|
// mmio header
|
|
println!("reading from mmio start!");
|
|
let header = ComponentMmioHeader {
|
|
component_count: U32::new(component_count as u32),
|
|
version: U16::new(0),
|
|
control: U16BE::new(vcpu.get_data().component_control.read().into_bits()),
|
|
};
|
|
if offset > size_of::<ComponentMmioHeader>() {
|
|
return 0;
|
|
}
|
|
let mut region = [0; size_of::<ComponentMmioHeader>() + size_of::<u64>()];
|
|
header.write_to_prefix(&mut region).unwrap();
|
|
let range = offset as usize..(offset + size) as usize;
|
|
return read_bytes_to_u64(®ion[range]);
|
|
}
|
|
|
|
if offset < vcpu.get_data().component_data_start {
|
|
// offsets
|
|
let range = offset as usize..(offset + size) as usize;
|
|
let components_range = OFFSETS_START..(size_of::<u32>() * component_count);
|
|
|
|
let range = range.start - components_range.start as usize..range.end - components_range.start as usize;
|
|
let first_offset_index = range.start >> 2;
|
|
let offset_buffer: [U32; 3] = std::array::from_fn(|index| {
|
|
(first_offset_index + index < component_count as usize)
|
|
.then_some(U32::new(
|
|
((first_offset_index + index) * INTERFACE_INTERVAL as usize) as u32,
|
|
))
|
|
.unwrap_or_default()
|
|
});
|
|
|
|
return read_bytes_to_u64(&offset_buffer.as_bytes()[range]);
|
|
}
|
|
|
|
// info region
|
|
|
|
let offset = offset - vcpu.get_data().component_data_start;
|
|
let index = offset / INTERFACE_INTERVAL as usize;
|
|
let offset = offset - (index * INTERFACE_INTERVAL as usize);
|
|
|
|
let Some(Component::Populated { interface, .. }) = vcpu.get_data().components.get(index) else {
|
|
return 0;
|
|
};
|
|
|
|
match size {
|
|
1 => interface.read_into::<u8>(offset).into(),
|
|
2 => interface.read_into::<U16>(offset).into(),
|
|
4 => interface.read_into::<U32>(offset).into(),
|
|
8 => interface.read_into::<U64>(offset).into(),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
pub fn component_mmio_write(vcpu: &mut Unicorn<CpuContext>, offset: u64, size: usize, value: u64) {
|
|
let offset = offset as usize;
|
|
let range = offset..offset + size;
|
|
|
|
if offset < OFFSETS_START {
|
|
if overlapping(
|
|
&range,
|
|
&(offset_of!(ComponentMmioHeader, control)..size_of::<ComponentMmioHeader>()),
|
|
) {
|
|
// control included
|
|
let mut buffer = [0; size_of::<ComponentMmioHeader>() + size_of::<u64>()];
|
|
write_bytes_from_u64(&mut buffer[range], value);
|
|
let header = ComponentMmioHeader::ref_from_prefix(&buffer).unwrap().0;
|
|
|
|
vcpu.get_data().component_control.write(ComponentMmioControl::from_bits(header.control.get()));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if offset < vcpu.get_data().component_data_start {
|
|
return;
|
|
}
|
|
|
|
let offset = offset - vcpu.get_data().component_data_start;
|
|
let index = offset / INTERFACE_INTERVAL as usize;
|
|
let offset = offset - (index * INTERFACE_INTERVAL as usize);
|
|
|
|
let Some(component) = vcpu.get_data().components.get(index) else {
|
|
return;
|
|
};
|
|
|
|
if offset > size_of::<ComponentInterface>() {
|
|
return;
|
|
}
|
|
|
|
component.perform_write(
|
|
range,
|
|
|interface| match size {
|
|
1 => interface.write_from(offset, &(value as u8)),
|
|
2 => interface.write_from(offset, &U16::new(value as u16)),
|
|
4 => interface.write_from(offset, &U32::new(value as u32)),
|
|
8 => interface.write_from(offset, &U64::new(value as u64)),
|
|
_ => unreachable!(),
|
|
},
|
|
vcpu.get_data().ram,
|
|
);
|
|
}
|