ax-cpu 0.6.6

Privileged instruction and structure abstractions for various CPU architectures
use crate::TrapFrame;

#[repr(C)]
#[derive(Debug, PartialEq, Eq)]
struct ExceptionTableEntry {
    #[cfg(any(
        target_arch = "aarch64",
        target_arch = "riscv64",
        target_arch = "x86_64"
    ))]
    from: i32,
    #[cfg(any(
        target_arch = "aarch64",
        target_arch = "riscv64",
        target_arch = "x86_64"
    ))]
    to: i32,
    #[cfg(not(any(
        target_arch = "aarch64",
        target_arch = "riscv64",
        target_arch = "x86_64"
    )))]
    from: usize,
    #[cfg(not(any(
        target_arch = "aarch64",
        target_arch = "riscv64",
        target_arch = "x86_64"
    )))]
    to: usize,
}

impl ExceptionTableEntry {
    #[inline]
    fn source_addr(&self) -> usize {
        #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
        {
            field_relative_addr(&self.from)
        }

        #[cfg(target_arch = "riscv64")]
        {
            let base = unsafe { _ex_table_start.as_ptr() } as isize;
            (base + self.from as isize) as usize
        }

        #[cfg(not(any(
            target_arch = "aarch64",
            target_arch = "riscv64",
            target_arch = "x86_64"
        )))]
        {
            self.from
        }
    }

    #[inline]
    fn to_addr(&self) -> usize {
        #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
        {
            field_relative_addr(&self.to)
        }

        #[cfg(target_arch = "riscv64")]
        {
            let base = unsafe { _ex_table_start.as_ptr() } as isize;
            (base + self.to as isize) as usize
        }

        #[cfg(not(any(
            target_arch = "aarch64",
            target_arch = "riscv64",
            target_arch = "x86_64"
        )))]
        {
            self.to
        }
    }
}

#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
#[inline]
fn field_relative_addr(offset: &i32) -> usize {
    let base = (offset as *const i32) as isize;
    (base + *offset as isize) as usize
}

unsafe extern "C" {
    static _ex_table_start: [ExceptionTableEntry; 0];
    static _ex_table_end: [ExceptionTableEntry; 0];
}

impl TrapFrame {
    pub(crate) fn fixup_exception(&mut self) -> bool {
        let entries = unsafe {
            core::slice::from_raw_parts(
                _ex_table_start.as_ptr(),
                _ex_table_end
                    .as_ptr()
                    .offset_from_unsigned(_ex_table_start.as_ptr()),
            )
        };
        #[cfg(target_arch = "x86_64")]
        {
            match entries
                .iter()
                .find(|entry| entry.source_addr() == self.ip())
            {
                Some(entry) => {
                    self.set_ip(entry.to_addr());
                    true
                }
                None => false,
            }
        }

        #[cfg(not(target_arch = "x86_64"))]
        {
            match entries.binary_search_by_key(&self.ip(), ExceptionTableEntry::source_addr) {
                Ok(entry) => {
                    self.set_ip(entries[entry].to_addr());
                    true
                }
                Err(_) => false,
            }
        }
    }
}

pub(crate) fn init_exception_table() {
    #[cfg(not(target_arch = "x86_64"))]
    {
        let ex_table = unsafe {
            core::slice::from_raw_parts_mut(
                _ex_table_start.as_ptr().cast_mut(),
                _ex_table_end
                    .as_ptr()
                    .offset_from_unsigned(_ex_table_start.as_ptr()),
            )
        };
        ex_table.sort_unstable_by_key(ExceptionTableEntry::source_addr);
    }
}