pub mod error;
pub mod hart2enc;
pub mod state;
pub mod step;
use crate::config::{self, AddressMode, Features};
use crate::packet::{payload, sync, unit};
use crate::types::Privilege;
use error::Error;
use payload::InstructionTrace;
#[derive(Clone, Debug)]
pub struct Generator<S, I = unit::ReferenceIOptions, D = unit::ReferenceDOptions>
where
S: step::Step,
I: unit::IOptions,
{
state: state::State,
features: Features,
options: Option<(I, D)>,
current: Option<S>,
previous: Option<(step::Kind, Privilege)>,
reported_exception: bool,
event: Option<Event>,
}
impl<S: step::Step + Clone, I: unit::IOptions + Clone, D: Clone> Generator<S, I, D> {
pub fn begin_qualification(
&mut self,
ioptions: I,
doptions: D,
) -> Result<sync::Support<I, D>, Error> {
ioptions
.update_features(&mut self.features)
.map_err(Error::UnsupportedFeature)?;
if self.features.implicit_returns {
return Err(Error::UnsupportedFeature("implicit return"));
}
if let Some(mode) = ioptions.address_mode() {
self.state.set_address_mode(mode);
}
self.options = Some((ioptions.clone(), doptions.clone()));
Ok(sync::Support {
ienable: true,
encoder_mode: sync::EncoderMode::BranchTrace,
qual_status: sync::QualStatus::NoChange,
ioptions,
denable: false,
dloss: false,
doptions,
})
}
pub fn end_qualification(&mut self, ienable: bool) -> Drain<'_, S, I, D> {
Drain::new(self, ienable)
}
pub fn process_step(
&mut self,
step: S,
event: Option<Event>,
) -> Result<Option<InstructionTrace<I, D>>, Error> {
if let Some(current) = self.current.as_mut() {
current.refine(&step);
}
self.do_step(Some(step), event).map(|p| {
if let Some(StepOutput::Payload(payload)) = p {
Some(payload)
} else {
None
}
})
}
fn do_step(
&mut self,
next: Option<S>,
next_event: Option<Event>,
) -> Result<Option<StepOutput<'_, I, D>>, Error> {
use hart2enc::CType;
use step::Kind;
let current = self.current.take();
self.current = next.clone();
let Some(current) = current else {
self.previous = None;
self.reported_exception = false;
self.event = None;
return Ok(None);
};
let kind = current.kind();
let previous = self.previous.take();
self.previous = Some((kind, current.context().privilege));
let reported_exception = self.reported_exception;
self.reported_exception = false;
let event = self.event.take();
self.event = next_event;
let mut builder =
self.state
.payload_builder(current.address(), current.context(), current.timestamp());
if let Kind::Branch { taken, .. } = kind {
builder.add_branch(taken)?;
}
if let Some((Kind::Trap { info, .. }, _)) = previous {
let payload = if kind.is_exc_only() {
builder.report_trap(false, info).into()
} else if reported_exception {
builder.report_sync().into()
} else {
builder.report_trap(true, info).into()
};
return Ok(Some(StepOutput::Payload(payload)));
}
if event == Some(Event::ReSync)
|| matches!(current.ctype(), CType::Precisely | CType::AsyncDiscon)
|| previous.map(|(_, p)| p) != Some(current.context().privilege)
{
return Ok(Some(StepOutput::Payload(builder.report_sync().into())));
}
let sijumps = self.features.sequentially_inferred_jumps;
if previous.map(|(k, _)| k.is_updiscon(sijumps)) == Some(true) {
return if let Kind::Trap {
insn_size: None,
info,
} = kind
{
self.reported_exception = true;
Ok(builder.report_trap(false, info).into())
} else {
let reason = if next
.as_ref()
.map(|n| {
next_event == Some(Event::ReSync)
|| matches!(n.kind(), Kind::Trap { .. })
|| !matches!(n.ctype(), CType::Unreported)
|| current.context().privilege != n.context().privilege
})
.unwrap_or(self.options.is_some())
{
state::Reason::Updiscon
} else {
state::Reason::Other
};
builder.report_address(reason)
}
.map(Into::into);
}
if event == Some(Event::Notify) {
return builder
.report_address(state::Reason::Notify)
.map(Into::into);
}
if let Kind::Trap { insn_size, .. } = kind {
return if insn_size.is_some() {
builder.report_address(state::Reason::Other).map(Into::into)
} else {
Ok(None)
};
}
let have_branches = builder.branches() != 0;
if next_event == Some(Event::ReSync) && have_branches {
return builder.report_address(state::Reason::Other).map(Into::into);
}
let Some(next) = next else {
return Ok(builder.into());
};
let ppccd = next.context().privilege != current.context().privilege
|| matches!(next.ctype(), CType::Precisely | CType::AsyncDiscon);
if next.kind().is_exc_only() || (ppccd && have_branches) {
return builder.report_address(state::Reason::Other).map(Into::into);
}
if let Some(branches) = builder.report_full_branchmap() {
return Ok(Some(StepOutput::Payload(branches.into())));
}
match current.ctype() {
CType::Imprecisely => Ok(Some(StepOutput::Payload(builder.context().into()))),
_ => Ok(None),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Event {
ReSync,
Notify,
}
#[derive(Debug)]
enum StepOutput<'s, I: unit::IOptions, D> {
Payload(InstructionTrace<I, D>),
Builder(state::PayloadBuilder<'s>),
}
impl<I: unit::IOptions, D> From<InstructionTrace<I, D>> for Option<StepOutput<'_, I, D>> {
fn from(payload: InstructionTrace<I, D>) -> Self {
Some(StepOutput::Payload(payload))
}
}
impl<'s, I: unit::IOptions, D> From<state::PayloadBuilder<'s>> for Option<StepOutput<'s, I, D>> {
fn from(builder: state::PayloadBuilder<'s>) -> Self {
Some(StepOutput::Builder(builder))
}
}
pub fn builder() -> Builder {
Default::default()
}
#[derive(Copy, Clone, Default)]
pub struct Builder {
features: Features,
address_mode: AddressMode,
}
impl Builder {
pub fn with_params(self, config: &config::Parameters) -> Self {
Self {
features: Features {
sequentially_inferred_jumps: config.sijump_p,
..self.features
},
..self
}
}
pub fn with_address_mode(self, mode: AddressMode) -> Self {
Self {
address_mode: mode,
..self
}
}
pub fn with_implicit_return(self, implicit_returns: bool) -> Self {
Self {
features: Features {
implicit_returns,
..self.features
},
..self
}
}
pub fn build<S, I, D>(&self) -> Result<Generator<S, I, D>, Error>
where
S: step::Step,
I: unit::IOptions,
{
let state = state::State::new(self.address_mode);
Ok(Generator {
state,
features: self.features,
options: None,
current: None,
previous: None,
reported_exception: false,
event: None,
})
}
}
#[derive(Debug)]
pub struct Drain<'g, S: step::Step, I: unit::IOptions, D> {
gen: &'g mut Generator<S, I, D>,
ienable: bool,
qual_status: Option<sync::QualStatus>,
}
impl<'g, S: step::Step, I: unit::IOptions, D> Drain<'g, S, I, D> {
fn new(gen: &'g mut Generator<S, I, D>, ienable: bool) -> Self {
Drain {
gen,
ienable,
qual_status: Some(sync::QualStatus::EndedRep),
}
}
}
impl<S: step::Step + Clone, I: unit::IOptions + Clone, D: Clone> Iterator for Drain<'_, S, I, D> {
type Item = Result<InstructionTrace<I, D>, Error>;
fn next(&mut self) -> Option<Self::Item> {
match self.gen.do_step(None, None) {
Ok(Some(StepOutput::Payload(p))) => {
self.qual_status = Some(sync::QualStatus::EndedNtr);
Some(Ok(p))
}
Ok(Some(StepOutput::Builder(b))) => Some(b.report_address(state::Reason::Other)),
Ok(None) => Option::zip(self.gen.options.clone(), self.qual_status.take()).map(
|((ioptions, doptions), qual_status)| {
Ok(sync::Support {
ienable: self.ienable,
encoder_mode: sync::EncoderMode::BranchTrace,
qual_status,
ioptions,
denable: false,
dloss: false,
doptions,
}
.into())
},
),
Err(e) => Some(Err(e)),
}
}
}