GolemComputers/rust/src/component/mmio.rs

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(&region[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,
);
}