use std::any::Any;
use std::io::{self, Cursor, Read, Write};
use std::net::{Ipv4Addr, TcpListener};
use super::super::run::Kept;
use super::super::{Answered, Core, Driven, Machine, Stretch};
use super::memory::Reads;
use super::packet::Carry;
use super::{Debugger, Map};
use crate::emulator::memory::Memory;
use crate::emulator::{Access, CoreSpec, Exit, Hit, Host, Stop, WalkThreads, Watch};
use crate::snapshot;
use crate::soc::{Endian, Region};
use crate::time::{NO_DEADLINE, VirtualNanos};
pub(crate) struct Fuzzed {
core: Core,
debugger: Debugger<'static>,
memory: Vec<Region>,
booted: Vec<u8>,
}
impl Fuzzed {
pub(crate) fn new(
spec: &CoreSpec,
memory: &[Region],
kernels: &'static [Box<dyn WalkThreads>],
) -> Self {
let endian = Endian::Little;
let cpu = (spec.processor)(endian).expect("a core the spec describes");
let map = Map::new(memory, &spec.aliases, &[], &cpu.own_ranges());
let mapped = Memory::new(memory, &spec.aliases, endian);
let rate = spec.instructions_per_second;
let mut machine = Machine::new(mapped, cpu, rate, Vec::new(), spec.boot.reset);
machine.map_memory();
machine.boot();
let booted = machine.cpu().save();
let listener =
TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).expect("a port on the loopback interface");
Self {
core: Core {
driven: Box::new(Unmodeled),
machine,
semihosting: true,
trace: None,
terminal: None,
},
debugger: Debugger::listen(&listener, false, map, kernels, None)
.expect("the listener just bound"),
memory: memory.to_vec(),
booted,
}
}
pub(crate) fn serve(&mut self, bytes: &[u8]) {
let Self {
core,
debugger,
memory,
booted,
} = self;
debugger.attach(Box::new(Fed(Cursor::new(bytes.to_vec()))));
for _ in 0..TURNS {
if debugger.connection.is_none() {
break;
}
debugger.keep_up(core);
if debugger.converse(core).is_err() {
debugger.disconnect(core);
break;
}
let Some(stretch) = core.next() else {
break;
};
debugger.hold_interrupts(core);
let stretch = match debugger.limit(core, stretch) {
Stretch::Run(instructions) => Stretch::Run(instructions.min(STRETCH)),
over @ Stretch::Over(_) => over,
};
let most = core.machine.retired() + u64::from(STRETCH);
let carry_until = debugger.carry_until(core).map(|until| until.min(most));
let (exit, ran) = core.run_stretch(stretch, carry_until);
let (exit, ran) = match exit {
Exit::HostCall => match core.host_call(Some(&mut *debugger)) {
Answered::Served => (exit, ran),
Answered::Unserved => {
let (exit, more) = core.run_stretch(Stretch::Run(0), carry_until);
(exit, ran + more)
}
Answered::Exited(_) => break,
},
_ => (exit, ran),
};
core.after(exit, ran);
if core.next().is_none() {
break;
}
debugger.after(core, exit, ran);
}
debugger.disconnect(core);
debugger.reads = Reads::Peeks;
for region in memory.iter() {
let size = usize::try_from(region.size).expect("a region fits in memory");
core.machine
.memory
.place(region.base, &vec![region.erased; size]);
core.machine.invalidate(region.base, region.size);
}
core.machine.unkept_written = false;
core.machine
.cpu()
.load(booted)
.expect("the state the processor saved");
core.machine.reset();
}
}
struct Fed(Cursor<Vec<u8>>);
impl Read for Fed {
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
self.0.read(bytes)
}
}
impl Write for Fed {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
Ok(bytes.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Carry for Fed {
fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> {
Ok(())
}
}
const TURNS: usize = 256;
const STRETCH: u32 = 64;
struct Unmodeled;
impl Driven for Unmodeled {
fn first_deadline(&mut self) -> VirtualNanos {
NO_DEADLINE
}
fn access(&mut self, _access: Access, _host: &mut dyn Host) -> (u64, VirtualNanos, bool) {
(0, NO_DEADLINE, true)
}
fn refused(&mut self) -> bool {
true
}
fn unwatched(&mut self, _watch: Watch) {}
fn stop(&mut self, _stop: Stop, _host: &mut dyn Host) -> Option<VirtualNanos> {
Some(NO_DEADLINE)
}
fn hit(&mut self, _hit: Hit, _host: &mut dyn Host) -> VirtualNanos {
NO_DEADLINE
}
fn into_any(self: Box<Self>) -> Box<dyn Any + Send> {
self
}
fn may_rewind(&self) -> bool {
true
}
fn keep(&self) -> Result<Kept, snapshot::Error> {
Ok(Kept::nothing())
}
fn put_back(&mut self, _kept: &Kept, _host: &mut dyn Host) -> Result<(), snapshot::Error> {
Ok(())
}
}