use core::mem::size_of;
use ax_errno::{AxError, LinuxError};
use ax_task::current;
use starry_vm::{VmMutPtr, VmPtr};
use crate::task::AsThread;
#[repr(C)]
#[derive(Clone, Copy)]
struct RseqArea {
cpu_id_start: u32,
cpu_id: u32,
rseq_cs: u64,
flags: u32,
padding: [u32; 3],
}
const RSEQ_AREA_SIZE: usize = size_of::<RseqArea>();
const RSEQ_AREA_ALIGN: usize = 32;
const RSEQ_FLAG_UNREGISTER: u32 = 1;
const RSEQ_CPU_ID_UNINITIALIZED: u32 = u32::MAX;
fn validate_rseq_args(addr: *mut u8, len: usize, flags: u32) -> Result<usize, AxError> {
if addr.is_null() || len != RSEQ_AREA_SIZE {
return Err(AxError::InvalidInput);
}
if flags & !RSEQ_FLAG_UNREGISTER != 0 {
return Err(AxError::InvalidInput);
}
let addr = addr.addr();
if !addr.is_multiple_of(RSEQ_AREA_ALIGN) {
return Err(AxError::InvalidInput);
}
Ok(addr)
}
fn ensure_rseq_area_accessible(addr: usize) -> Result<(), AxError> {
let area = addr as *mut RseqArea;
let _ = area.vm_read_uninit().map_err(|_| AxError::BadAddress)?;
area.vm_write(RseqArea {
cpu_id_start: 0,
cpu_id: RSEQ_CPU_ID_UNINITIALIZED,
rseq_cs: 0,
flags: 0,
padding: [0; 3],
})
.map_err(|_| AxError::BadAddress)?;
Ok(())
}
pub fn sys_rseq(addr: *mut u8, len: usize, flags: u32, sig: u32) -> Result<isize, AxError> {
debug!(
"sys_rseq <= addr: {:?}, len: {}, flags: {}, sig: {}",
addr, len, flags, sig
);
let addr = validate_rseq_args(addr, len, flags)?;
let curr = current();
let thr = curr.as_thread();
let registered_addr = thr.rseq_area();
let unregister = flags & RSEQ_FLAG_UNREGISTER != 0;
if unregister {
if registered_addr == 0 || registered_addr != addr || thr.rseq_signature() != sig {
return Err(AxError::InvalidInput);
}
thr.clear_rseq_state();
return Ok(0);
}
if registered_addr != 0 {
return Err(AxError::from(LinuxError::EBUSY));
}
ensure_rseq_area_accessible(addr)?;
thr.set_rseq_state(addr, sig);
Ok(0)
}
#[cfg(test)]
mod tests {
use ax_errno::AxError;
use super::{RSEQ_AREA_SIZE, RSEQ_FLAG_UNREGISTER, validate_rseq_args};
#[test]
fn validate_rseq_args_rejects_null_addr() {
assert_eq!(
validate_rseq_args(core::ptr::null_mut(), RSEQ_AREA_SIZE, 0).unwrap_err(),
AxError::InvalidInput
);
}
#[test]
fn validate_rseq_args_rejects_bad_len() {
let ptr = 0x1000 as *mut u8;
assert_eq!(
validate_rseq_args(ptr, RSEQ_AREA_SIZE - 1, 0).unwrap_err(),
AxError::InvalidInput
);
}
#[test]
fn validate_rseq_args_rejects_bad_flags() {
let ptr = 0x1000 as *mut u8;
assert_eq!(
validate_rseq_args(ptr, RSEQ_AREA_SIZE, RSEQ_FLAG_UNREGISTER << 1).unwrap_err(),
AxError::InvalidInput
);
}
#[test]
fn validate_rseq_args_rejects_misaligned_addr() {
let ptr = 0x1001 as *mut u8;
assert_eq!(
validate_rseq_args(ptr, RSEQ_AREA_SIZE, 0).unwrap_err(),
AxError::InvalidInput
);
}
#[test]
fn validate_rseq_args_accepts_aligned_addr() {
let ptr = 0x1000 as *mut u8;
assert_eq!(validate_rseq_args(ptr, RSEQ_AREA_SIZE, 0).unwrap(), 0x1000);
}
}