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( &self, range: Range, writer: impl FnOnce(&UnsyncCell), ram: Arc>, java_vm: JavaVM, ) -> Option>>> { 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, 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::() { return 0; } let mut region = [0; size_of::() + size_of::()]; 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::() * 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::(offset).into(), 2 => interface.read_into::(offset).into(), 4 => interface.read_into::(offset).into(), 8 => interface.read_into::(offset).into(), _ => unreachable!(), } } pub fn component_mmio_write(vcpu: &mut Unicorn, 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::()), ) { // control included let mut buffer = [0; size_of::() + size_of::()]; 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::() { 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, ); }