mod breakpoint;
mod monitor;
use gdbstub::target::{
Target, TargetError, TargetResult,
ext::{
self,
base::{
single_register_access::{SingleRegisterAccess, SingleRegisterAccessOps},
singlethread::{SingleThreadBase, SingleThreadResume, SingleThreadResumeOps},
},
breakpoints::{self, BreakpointsOps},
},
};
use spin::Mutex;
use crate::{
ExceptionInfo,
arch::{DebuggerArch, SystemArch, UefiArchRegs},
memory,
system::SystemState,
};
const WINDBG_MOCK_ADDRESSES: [u64; 3] = [0xfffff78000000268, 0, 0x34c00];
pub struct PatinaTarget {
exception_info: ExceptionInfo,
resume: bool,
reboot: bool,
disable_checks: bool,
system_state: &'static Mutex<SystemState>,
}
impl PatinaTarget {
pub fn new(exception_info: ExceptionInfo, system_state: &'static Mutex<SystemState>) -> Self {
PatinaTarget { exception_info, resume: false, reboot: false, disable_checks: false, system_state }
}
pub fn is_resumed(&self) -> bool {
self.resume
}
pub fn reboot_on_resume(&self) -> bool {
self.reboot
}
pub fn into_exception_info(self) -> ExceptionInfo {
self.exception_info
}
}
impl Target for PatinaTarget {
type Arch = SystemArch;
type Error = ();
fn base_ops(&mut self) -> gdbstub::target::ext::base::BaseOps<'_, Self::Arch, Self::Error> {
gdbstub::target::ext::base::BaseOps::SingleThread(self)
}
#[inline(always)]
fn use_no_ack_mode(&self) -> bool {
false
}
#[inline(always)]
fn use_rle(&self) -> bool {
false
}
#[inline(always)]
fn use_x_upcase_packet(&self) -> bool {
false
}
#[inline(always)]
fn support_breakpoints(&mut self) -> Option<BreakpointsOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_monitor_cmd(&mut self) -> Option<ext::monitor_cmd::MonitorCmdOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_target_description_xml_override(
&mut self,
) -> Option<ext::target_description_xml_override::TargetDescriptionXmlOverrideOps<'_, Self>> {
Some(self)
}
}
impl SingleThreadBase for PatinaTarget {
fn read_registers(&mut self, regs: &mut <Self::Arch as gdbstub::arch::Arch>::Registers) -> TargetResult<(), Self> {
regs.read_from_context(&self.exception_info.context);
Ok(())
}
fn write_registers(&mut self, regs: &<Self::Arch as gdbstub::arch::Arch>::Registers) -> TargetResult<(), Self> {
regs.write_to_context(&mut self.exception_info.context);
Ok(())
}
fn read_addrs(
&mut self,
start_addr: <Self::Arch as gdbstub::arch::Arch>::Usize,
data: &mut [u8],
) -> TargetResult<usize, Self> {
if WINDBG_MOCK_ADDRESSES.contains(&start_addr) {
data.fill(0);
return Ok(data.len());
}
match memory::read_memory::<SystemArch>(start_addr, data, self.disable_checks) {
Ok(bytes_read) => Ok(bytes_read),
Err(_) => {
log::info!("Failed to read memory at 0x{:x} : 0x{:x}", start_addr, data.len());
Err(gdbstub::target::TargetError::NonFatal)
}
}
}
fn write_addrs(
&mut self,
start_addr: <Self::Arch as gdbstub::arch::Arch>::Usize,
data: &[u8],
) -> TargetResult<(), Self> {
match memory::write_memory::<SystemArch>(start_addr, data) {
Ok(_) => Ok(()),
Err(_) => {
log::info!("Failed to write memory at 0x{:x} : 0x{:x}", start_addr, data.len());
Err(gdbstub::target::TargetError::NonFatal)
}
}
}
#[inline(always)]
fn support_resume(&mut self) -> Option<SingleThreadResumeOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_single_register_access(&mut self) -> Option<SingleRegisterAccessOps<'_, (), Self>> {
Some(self)
}
}
impl SingleRegisterAccess<()> for PatinaTarget {
fn read_register(
&mut self,
_tid: (),
reg_id: <Self::Arch as gdbstub::arch::Arch>::RegId,
buf: &mut [u8],
) -> TargetResult<usize, Self> {
<Self::Arch as gdbstub::arch::Arch>::Registers::read_register_from_context(
&self.exception_info.context,
reg_id,
buf,
)
.map_err(|_| gdbstub::target::TargetError::NonFatal)
}
fn write_register(
&mut self,
_tid: (),
reg_id: <Self::Arch as gdbstub::arch::Arch>::RegId,
val: &[u8],
) -> TargetResult<(), Self> {
<Self::Arch as gdbstub::arch::Arch>::Registers::write_register_to_context(
&mut self.exception_info.context,
reg_id,
val,
)
.map_err(|_| gdbstub::target::TargetError::NonFatal)
}
}
impl SingleThreadResume for PatinaTarget {
fn resume(&mut self, _signal: Option<gdbstub::common::Signal>) -> Result<(), Self::Error> {
self.resume = true;
Ok(())
}
#[inline(always)]
fn support_single_step(&mut self) -> Option<ext::base::singlethread::SingleThreadSingleStepOps<'_, Self>> {
Some(self)
}
}
impl ext::base::singlethread::SingleThreadSingleStep for PatinaTarget {
fn step(&mut self, _signal: Option<gdbstub::common::Signal>) -> Result<(), Self::Error> {
SystemArch::set_single_step(&mut self.exception_info);
self.resume = true;
Ok(())
}
}
impl breakpoints::Breakpoints for PatinaTarget {
#[inline(always)]
fn support_sw_breakpoint(&mut self) -> Option<breakpoints::SwBreakpointOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_hw_watchpoint(&mut self) -> Option<breakpoints::HwWatchpointOps<'_, Self>> {
Some(self)
}
}
impl ext::target_description_xml_override::TargetDescriptionXmlOverride for PatinaTarget {
fn target_description_xml(
&self,
annex: &[u8],
offset: u64,
length: usize,
buf: &mut [u8],
) -> TargetResult<usize, Self> {
let offset = offset as usize;
let xml = match annex {
b"target.xml" => SystemArch::GDB_TARGET_XML,
b"registers.xml" => SystemArch::GDB_REGISTERS_XML,
_ => return Err(TargetError::NonFatal),
};
let bytes = xml.trim().as_bytes();
if offset >= bytes.len() {
return Ok(0);
}
let start = offset;
let end = (start + length).min(bytes.len());
let copy_bytes: &[u8] = &bytes[start..end];
let copy_len = copy_bytes.len().min(buf.len());
buf[..copy_len].copy_from_slice(©_bytes[..copy_len]);
Ok(copy_len)
}
}