use std::collections::BTreeMap;
use super::{Fd, Kernel, err};
use crate::abi::Arch;
use crate::abi::errno::Errno;
use crate::vcpu::GuestMemory;
const POLLIN: u32 = 0x0001;
const POLLOUT: u32 = 0x0004;
const POLLERR: u32 = 0x0008;
const POLLHUP: u32 = 0x0010;
const POLLNVAL: u32 = 0x0020;
#[derive(Debug, Default)]
pub(super) struct EventFdInst {
count: u64,
semaphore: bool,
nonblock: bool,
}
#[derive(Debug, Default)]
pub(super) struct TimerFdInst {
expiry_ns: Option<u128>,
interval_ns: u128,
expirations: u64,
nonblock: bool,
}
#[derive(Debug, Clone, Copy)]
struct EpollWatch {
events: u32,
data: u64,
}
#[derive(Debug, Default)]
pub(super) struct EpollInst {
interest: BTreeMap<i32, EpollWatch>,
}
fn now_ns() -> u128 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos()
}
fn read_u16(mem: &GuestMemory, addr: u64) -> Option<u16> {
let b = mem.read_vec(addr, 2).ok()?;
Some(u16::from_le_bytes([b[0], b[1]]))
}
fn write_u16(mem: &mut GuestMemory, addr: u64, v: u16) -> bool {
mem.write(addr, &v.to_le_bytes()).is_ok()
}
fn read_timespec(mem: &GuestMemory, ptr: u64) -> Option<(u64, u64)> {
let sec = mem.read_u64(ptr).ok()?;
let nsec = mem.read_u64(ptr + 8).ok()?;
Some((sec, nsec))
}
fn write_timespec(mem: &mut GuestMemory, ptr: u64, sec: u64, nsec: u64) -> bool {
mem.write(ptr, &sec.to_le_bytes()).is_ok() && mem.write(ptr + 8, &nsec.to_le_bytes()).is_ok()
}
fn fdset_word_count(nfds: u64) -> u64 {
nfds.div_ceil(64)
}
fn read_fdset(mem: &GuestMemory, ptr: u64, nfds: u64) -> Option<Vec<bool>> {
if ptr == 0 {
return Some(vec![false; nfds as usize]);
}
let mut bits = Vec::with_capacity(nfds as usize);
for i in 0..nfds {
let word = mem.read_u64(ptr + (i / 64) * 8).ok()?;
bits.push(word & (1u64 << (i % 64)) != 0);
}
Some(bits)
}
fn write_fdset(mem: &mut GuestMemory, ptr: u64, nfds: u64, bits: &[bool]) -> bool {
if ptr == 0 {
return true;
}
let words = fdset_word_count(nfds) as usize;
let mut out = vec![0u64; words];
for (i, &b) in bits.iter().enumerate() {
if b {
out[i / 64] |= 1u64 << (i % 64);
}
}
for (w, val) in out.iter().enumerate() {
if mem.write(ptr + (w as u64) * 8, &val.to_le_bytes()).is_err() {
return false;
}
}
true
}
impl Kernel {
fn fd_ready(&mut self, fd_num: i32) -> u32 {
let Some(fd) = self.cur.fds.get(fd_num).cloned() else {
return POLLNVAL;
};
match fd {
Fd::Stdin => POLLIN,
Fd::Stdout | Fd::Stderr => POLLOUT,
Fd::File { .. } | Fd::Dir { .. } | Fd::Socket { .. } => POLLIN | POLLOUT,
Fd::PipeRead(i) => {
let p = &self.pipes[i];
if !p.buf.is_empty() {
POLLIN
} else if p.writers == 0 {
POLLIN | POLLHUP
} else {
0
}
}
Fd::PipeWrite(i) => {
if self.pipes[i].readers == 0 {
POLLERR
} else {
POLLOUT
}
}
Fd::Eventfd(i) => {
let mut m = POLLOUT;
if self.eventfds[i].count > 0 {
m |= POLLIN;
}
m
}
Fd::Timerfd(i) => {
self.update_timerfd(i);
if self.timerfds[i].expirations > 0 {
POLLIN
} else {
0
}
}
Fd::Epoll(_) => 0,
}
}
pub(super) fn sys_poll(
&mut self,
fds_ptr: u64,
nfds: u64,
timeout_ms: i64,
mem: &mut GuestMemory,
) -> i64 {
const STRIDE: u64 = 8; let mut entries = Vec::with_capacity(nfds as usize);
for i in 0..nfds {
let addr = fds_ptr + i * STRIDE;
let Ok(fd_raw) = mem.read_u32(addr) else {
return err(Errno::EFAULT);
};
let Some(events) = read_u16(mem, addr + 4) else {
return err(Errno::EFAULT);
};
entries.push((addr, fd_raw as i32, events));
}
let mut ready_count = 0i64;
for (addr, fd, events) in entries {
let revents = if fd < 0 {
0
} else {
self.fd_ready(fd) & (u32::from(events) | POLLERR | POLLHUP | POLLNVAL)
};
if !write_u16(mem, addr + 6, revents as u16) {
return err(Errno::EFAULT);
}
if revents != 0 {
ready_count += 1;
}
}
if ready_count > 0 || timeout_ms == 0 {
return ready_count;
}
self.block = true;
0
}
pub(super) fn sys_ppoll(
&mut self,
fds_ptr: u64,
nfds: u64,
timeout_ts: u64,
_sigmask: u64,
_sigsetsize: u64,
mem: &mut GuestMemory,
) -> i64 {
let timeout_ms = if timeout_ts == 0 {
-1
} else {
let Some((sec, nsec)) = read_timespec(mem, timeout_ts) else {
return err(Errno::EFAULT);
};
i64::from(sec != 0 || nsec != 0)
};
self.sys_poll(fds_ptr, nfds, timeout_ms, mem)
}
fn sys_select_core(
&mut self,
nfds: u64,
r: u64,
w: u64,
e: u64,
immediate: bool,
mem: &mut GuestMemory,
) -> i64 {
let Some(rbits) = read_fdset(mem, r, nfds) else {
return err(Errno::EFAULT);
};
let Some(wbits) = read_fdset(mem, w, nfds) else {
return err(Errno::EFAULT);
};
let Some(ebits) = read_fdset(mem, e, nfds) else {
return err(Errno::EFAULT);
};
let mut rout = vec![false; nfds as usize];
let mut wout = vec![false; nfds as usize];
let mut eout = vec![false; nfds as usize];
let mut total = 0i64;
for fd in 0..nfds as usize {
if !rbits[fd] && !wbits[fd] && !ebits[fd] {
continue;
}
let ready = self.fd_ready(fd as i32);
if rbits[fd] && ready & (POLLIN | POLLHUP) != 0 {
rout[fd] = true;
total += 1;
}
if wbits[fd] && ready & POLLOUT != 0 {
wout[fd] = true;
total += 1;
}
if ebits[fd] && ready & POLLERR != 0 {
eout[fd] = true;
total += 1;
}
}
if total > 0 || immediate {
if !write_fdset(mem, r, nfds, &rout)
|| !write_fdset(mem, w, nfds, &wout)
|| !write_fdset(mem, e, nfds, &eout)
{
return err(Errno::EFAULT);
}
return total;
}
self.block = true;
0
}
#[allow(clippy::too_many_arguments)] pub(super) fn sys_pselect6(
&mut self,
nfds: u64,
r: u64,
w: u64,
e: u64,
timeout_ts: u64,
_sigmask: u64,
mem: &mut GuestMemory,
) -> i64 {
let immediate = if timeout_ts == 0 {
false
} else {
let Some((sec, nsec)) = read_timespec(mem, timeout_ts) else {
return err(Errno::EFAULT);
};
sec == 0 && nsec == 0
};
self.sys_select_core(nfds, r, w, e, immediate, mem)
}
pub(super) fn sys_select(
&mut self,
nfds: u64,
r: u64,
w: u64,
e: u64,
timeout_tv: u64,
mem: &mut GuestMemory,
) -> i64 {
let immediate = if timeout_tv == 0 {
false
} else {
let (Ok(sec), Ok(usec)) = (mem.read_u64(timeout_tv), mem.read_u64(timeout_tv + 8))
else {
return err(Errno::EFAULT);
};
sec == 0 && usec == 0
};
self.sys_select_core(nfds, r, w, e, immediate, mem)
}
fn epoll_event_layout(&self) -> (u64, u64) {
match self.arch {
Arch::X86_64 => (4, 12),
Arch::Aarch64 => (8, 16),
}
}
fn read_epoll_event(&self, mem: &GuestMemory, ptr: u64) -> Option<(u32, u64)> {
let events = mem.read_u32(ptr).ok()?;
let (data_off, _) = self.epoll_event_layout();
let data = mem.read_u64(ptr + data_off).ok()?;
Some((events, data))
}
fn write_epoll_event(&self, mem: &mut GuestMemory, ptr: u64, events: u32, data: u64) -> bool {
let (data_off, _) = self.epoll_event_layout();
mem.write(ptr, &events.to_le_bytes()).is_ok()
&& mem.write(ptr + data_off, &data.to_le_bytes()).is_ok()
}
pub(super) fn sys_epoll_create1(&mut self, _flags: u64) -> i64 {
let idx = self.epolls.len();
self.epolls.push(EpollInst::default());
i64::from(self.cur.fds.alloc(Fd::Epoll(idx)))
}
pub(super) fn sys_epoll_ctl(
&mut self,
epfd: u64,
op: u64,
fd: u64,
event_ptr: u64,
mem: &GuestMemory,
) -> i64 {
const EPOLL_CTL_ADD: u64 = 1;
const EPOLL_CTL_DEL: u64 = 2;
const EPOLL_CTL_MOD: u64 = 3;
let Some(Fd::Epoll(idx)) = self.cur.fds.get(epfd as i32).cloned() else {
return err(Errno::EBADF);
};
if self.cur.fds.get(fd as i32).is_none() {
return err(Errno::EBADF);
}
let target = fd as i32;
match op {
EPOLL_CTL_ADD => {
if self.epolls[idx].interest.contains_key(&target) {
return err(Errno::EEXIST);
}
let Some((events, data)) = self.read_epoll_event(mem, event_ptr) else {
return err(Errno::EFAULT);
};
self.epolls[idx]
.interest
.insert(target, EpollWatch { events, data });
0
}
EPOLL_CTL_MOD => {
if !self.epolls[idx].interest.contains_key(&target) {
return err(Errno::ENOENT);
}
let Some((events, data)) = self.read_epoll_event(mem, event_ptr) else {
return err(Errno::EFAULT);
};
self.epolls[idx]
.interest
.insert(target, EpollWatch { events, data });
0
}
EPOLL_CTL_DEL => {
if self.epolls[idx].interest.remove(&target).is_none() {
return err(Errno::ENOENT);
}
0
}
_ => err(Errno::EINVAL),
}
}
pub(super) fn sys_epoll_wait(
&mut self,
epfd: u64,
events_ptr: u64,
maxevents: u64,
timeout_ms: i64,
mem: &mut GuestMemory,
) -> i64 {
let Some(Fd::Epoll(idx)) = self.cur.fds.get(epfd as i32).cloned() else {
return err(Errno::EBADF);
};
if maxevents == 0 {
return err(Errno::EINVAL);
}
let watches: Vec<(i32, EpollWatch)> = self.epolls[idx]
.interest
.iter()
.map(|(&fd, &w)| (fd, w))
.collect();
let (_, stride) = self.epoll_event_layout();
let mut n = 0u64;
for (fd, w) in watches {
if n >= maxevents {
break;
}
let ready = self.fd_ready(fd) & (w.events | POLLERR | POLLHUP);
if ready == 0 {
continue;
}
let addr = events_ptr + n * stride;
if !self.write_epoll_event(mem, addr, ready, w.data) {
return err(Errno::EFAULT);
}
n += 1;
}
if n > 0 || timeout_ms == 0 {
return n as i64;
}
self.block = true;
0
}
pub(super) fn sys_epoll_pwait2(
&mut self,
epfd: u64,
events_ptr: u64,
maxevents: u64,
timeout_ts: u64,
mem: &mut GuestMemory,
) -> i64 {
let timeout_ms = if timeout_ts == 0 {
-1
} else {
let Some((sec, nsec)) = read_timespec(mem, timeout_ts) else {
return err(Errno::EFAULT);
};
i64::from(sec != 0 || nsec != 0)
};
self.sys_epoll_wait(epfd, events_ptr, maxevents, timeout_ms, mem)
}
pub(super) fn sys_eventfd2(&mut self, initval: u64, flags: u64) -> i64 {
const EFD_SEMAPHORE: u64 = 1;
const EFD_NONBLOCK: u64 = 0o4000;
let idx = self.eventfds.len();
self.eventfds.push(EventFdInst {
count: initval,
semaphore: flags & EFD_SEMAPHORE != 0,
nonblock: flags & EFD_NONBLOCK != 0,
});
i64::from(self.cur.fds.alloc(Fd::Eventfd(idx)))
}
pub(super) fn read_eventfd(
&mut self,
i: usize,
buf: u64,
count: u64,
mem: &mut GuestMemory,
) -> i64 {
if count < 8 {
return err(Errno::EINVAL);
}
if self.eventfds[i].count == 0 {
if self.eventfds[i].nonblock {
return err(Errno::EAGAIN);
}
self.block = true;
return 0;
}
let value = if self.eventfds[i].semaphore {
self.eventfds[i].count -= 1;
1u64
} else {
std::mem::replace(&mut self.eventfds[i].count, 0)
};
if mem.write(buf, &value.to_le_bytes()).is_err() {
return err(Errno::EFAULT);
}
8
}
pub(super) fn write_eventfd(&mut self, i: usize, data: &[u8]) -> i64 {
if data.len() < 8 {
return err(Errno::EINVAL);
}
let mut b = [0u8; 8];
b.copy_from_slice(&data[..8]);
let value = u64::from_le_bytes(b);
if value == u64::MAX {
return err(Errno::EINVAL);
}
let cur = self.eventfds[i].count;
match cur.checked_add(value) {
Some(sum) if sum < u64::MAX => {
self.eventfds[i].count = sum;
8
}
_ if self.eventfds[i].nonblock => err(Errno::EAGAIN),
_ => {
self.block = true;
0
}
}
}
pub(super) fn sys_timerfd_create(&mut self, _clockid: u64, flags: u64) -> i64 {
const TFD_NONBLOCK: u64 = 0o4000;
let idx = self.timerfds.len();
self.timerfds.push(TimerFdInst {
expiry_ns: None,
interval_ns: 0,
expirations: 0,
nonblock: flags & TFD_NONBLOCK != 0,
});
i64::from(self.cur.fds.alloc(Fd::Timerfd(idx)))
}
fn update_timerfd(&mut self, i: usize) {
let Some(expiry) = self.timerfds[i].expiry_ns else {
return;
};
let now = now_ns();
if now < expiry {
return;
}
let interval = self.timerfds[i].interval_ns;
if let Some(periods) = (now - expiry).checked_div(interval) {
let elapsed = periods + 1;
self.timerfds[i].expirations = self.timerfds[i]
.expirations
.saturating_add(u64::try_from(elapsed).unwrap_or(u64::MAX));
self.timerfds[i].expiry_ns = Some(expiry + elapsed * interval);
} else {
self.timerfds[i].expirations = self.timerfds[i].expirations.saturating_add(1);
self.timerfds[i].expiry_ns = None;
}
}
fn timerfd_remaining(&mut self, i: usize) -> (u64, u64) {
self.update_timerfd(i);
match self.timerfds[i].expiry_ns {
None => (0, 0),
Some(exp) => {
let now = now_ns();
let rem = exp.saturating_sub(now);
(
u64::try_from(rem / 1_000_000_000).unwrap_or(u64::MAX),
(rem % 1_000_000_000) as u64,
)
}
}
}
pub(super) fn sys_timerfd_settime(
&mut self,
fd: u64,
flags: u64,
new_value: u64,
old_value: u64,
mem: &mut GuestMemory,
) -> i64 {
const TFD_TIMER_ABSTIME: u64 = 1;
let Some(Fd::Timerfd(i)) = self.cur.fds.get(fd as i32).cloned() else {
return err(Errno::EBADF);
};
let Some((int_sec, int_nsec)) = read_timespec(mem, new_value) else {
return err(Errno::EFAULT);
};
let Some((val_sec, val_nsec)) = read_timespec(mem, new_value + 16) else {
return err(Errno::EFAULT);
};
if int_nsec >= 1_000_000_000 || val_nsec >= 1_000_000_000 {
return err(Errno::EINVAL);
}
if old_value != 0 {
let interval_ns = self.timerfds[i].interval_ns;
let (rem_sec, rem_nsec) = self.timerfd_remaining(i);
if !write_timespec(
mem,
old_value,
u64::try_from(interval_ns / 1_000_000_000).unwrap_or(u64::MAX),
(interval_ns % 1_000_000_000) as u64,
) || !write_timespec(mem, old_value + 16, rem_sec, rem_nsec)
{
return err(Errno::EFAULT);
}
}
let interval_ns = u128::from(int_sec) * 1_000_000_000 + u128::from(int_nsec);
let value_ns = u128::from(val_sec) * 1_000_000_000 + u128::from(val_nsec);
self.timerfds[i].expiry_ns = if value_ns == 0 {
None
} else if flags & TFD_TIMER_ABSTIME != 0 {
Some(value_ns)
} else {
Some(now_ns() + value_ns)
};
self.timerfds[i].interval_ns = interval_ns;
self.timerfds[i].expirations = 0;
0
}
pub(super) fn sys_timerfd_gettime(
&mut self,
fd: u64,
curr_value: u64,
mem: &mut GuestMemory,
) -> i64 {
let Some(Fd::Timerfd(i)) = self.cur.fds.get(fd as i32).cloned() else {
return err(Errno::EBADF);
};
let interval_ns = self.timerfds[i].interval_ns;
let (sec, nsec) = self.timerfd_remaining(i);
if !write_timespec(
mem,
curr_value,
u64::try_from(interval_ns / 1_000_000_000).unwrap_or(u64::MAX),
(interval_ns % 1_000_000_000) as u64,
) || !write_timespec(mem, curr_value + 16, sec, nsec)
{
return err(Errno::EFAULT);
}
0
}
pub(super) fn read_timerfd(
&mut self,
i: usize,
buf: u64,
count: u64,
mem: &mut GuestMemory,
) -> i64 {
if count < 8 {
return err(Errno::EINVAL);
}
self.update_timerfd(i);
if self.timerfds[i].expirations == 0 {
if self.timerfds[i].nonblock {
return err(Errno::EAGAIN);
}
self.block = true;
return 0;
}
let val = self.timerfds[i].expirations;
self.timerfds[i].expirations = 0;
if mem.write(buf, &val.to_le_bytes()).is_err() {
return err(Errno::EFAULT);
}
8
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::abi::Arch;
use crate::abi::arch::Sysno;
use crate::fs::{MountTable, TmpFs};
use crate::vcpu::mem::Prot;
use crate::vcpu::{Exit, Vcpu, VcpuError};
#[derive(Clone)]
struct DummyVcpu;
impl Vcpu for DummyVcpu {
fn run(&mut self, _m: &mut GuestMemory) -> Result<Exit, VcpuError> {
Ok(Exit::Halt)
}
fn syscall_nr(&self) -> u64 {
0
}
fn syscall_args(&self) -> [u64; 6] {
[0; 6]
}
fn set_syscall_ret(&mut self, _v: u64) {}
fn reg(&self, _i: usize) -> u64 {
0
}
fn set_reg(&mut self, _i: usize, _v: u64) {}
fn pc(&self) -> u64 {
0
}
fn set_pc(&mut self, _v: u64) {}
fn sp(&self) -> u64 {
0
}
fn set_sp(&mut self, _v: u64) {}
fn set_tls(&mut self, _v: u64) {}
fn fork(&self) -> Box<dyn Vcpu> {
Box::new(self.clone())
}
fn reset(&mut self, _e: u64, _s: u64) {}
}
const PAGE: u64 = 4096;
fn setup() -> (Kernel, GuestMemory, DummyVcpu) {
let mut mounts = MountTable::new();
mounts.mount("/", Box::new(TmpFs::new()));
let mut kernel = Kernel::new(Arch::Aarch64, mounts);
kernel.cur.pid = 1;
let mut mem = GuestMemory::new(0x1_0000, 16 * PAGE);
mem.map(0x1_0000, 4 * PAGE, Prot::rw()).unwrap();
(kernel, mem, DummyVcpu)
}
fn call(
k: &mut Kernel,
mem: &mut GuestMemory,
v: &mut DummyVcpu,
s: Sysno,
a: [u64; 6],
) -> i64 {
k.dispatch(s, 0, &a, v, mem)
}
#[test]
fn eventfd_write_then_read_counter() {
let (mut k, mut mem, mut v) = setup();
let fd = call(
&mut k,
&mut mem,
&mut v,
Sysno::Eventfd2,
[0, 0, 0, 0, 0, 0],
);
assert!(fd >= 3);
let fd = fd as u64;
let buf = 0x1_0000;
mem.write_init(buf, &3u64.to_le_bytes()).unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[fd, buf, 8, 0, 0, 0]
),
8
);
mem.write_init(buf, &4u64.to_le_bytes()).unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[fd, buf, 8, 0, 0, 0]
),
8
);
let out = 0x1_1000;
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Read, [fd, out, 8, 0, 0, 0]),
8
);
assert_eq!(mem.read_u64(out).unwrap(), 7);
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Read, [fd, out, 8, 0, 0, 0]),
0
);
assert!(k.block);
}
#[test]
fn eventfd_semaphore_mode_decrements_by_one() {
const EFD_SEMAPHORE: u64 = 1;
let (mut k, mut mem, mut v) = setup();
let fd = call(
&mut k,
&mut mem,
&mut v,
Sysno::Eventfd2,
[5, EFD_SEMAPHORE, 0, 0, 0, 0],
) as u64;
let out = 0x1_0000;
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Read, [fd, out, 8, 0, 0, 0]),
8
);
assert_eq!(mem.read_u64(out).unwrap(), 1);
}
#[test]
fn poll_reports_pollin_when_pipe_has_data() {
let (mut k, mut mem, mut v) = setup();
let fds = 0x1_0000;
call(&mut k, &mut mem, &mut v, Sysno::Pipe2, [fds, 0, 0, 0, 0, 0]);
let rfd = u64::from(mem.read_u32(fds).unwrap());
let wfd = u64::from(mem.read_u32(fds + 4).unwrap());
let msg = 0x1_1000;
mem.write_init(msg, b"hi").unwrap();
call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[wfd, msg, 2, 0, 0, 0],
);
let pollfds = 0x1_2000;
mem.write_init(pollfds, &(rfd as u32).to_le_bytes())
.unwrap();
mem.write_init(pollfds + 4, &1u16.to_le_bytes()).unwrap(); mem.write_init(pollfds + 6, &0u16.to_le_bytes()).unwrap();
let n = call(
&mut k,
&mut mem,
&mut v,
Sysno::Poll,
[pollfds, 1, 0, 0, 0, 0],
);
assert_eq!(n, 1);
assert_eq!(mem.read_vec(pollfds + 6, 2).unwrap(), 1u16.to_le_bytes());
}
#[test]
fn poll_zero_timeout_on_empty_pipe_returns_immediately() {
let (mut k, mut mem, mut v) = setup();
let fds = 0x1_0000;
call(&mut k, &mut mem, &mut v, Sysno::Pipe2, [fds, 0, 0, 0, 0, 0]);
let rfd = u64::from(mem.read_u32(fds).unwrap());
let pollfds = 0x1_2000;
mem.write_init(pollfds, &(rfd as u32).to_le_bytes())
.unwrap();
mem.write_init(pollfds + 4, &1u16.to_le_bytes()).unwrap();
let n = call(
&mut k,
&mut mem,
&mut v,
Sysno::Poll,
[pollfds, 1, 0, 0, 0, 0],
);
assert_eq!(n, 0, "no data and writer still open, but timeout=0");
assert!(!k.block, "zero timeout must not block");
}
#[test]
fn epoll_create_ctl_wait_on_ready_pipe() {
let (mut k, mut mem, mut v) = setup();
let fds = 0x1_0000;
call(&mut k, &mut mem, &mut v, Sysno::Pipe2, [fds, 0, 0, 0, 0, 0]);
let rfd = u64::from(mem.read_u32(fds).unwrap());
let wfd = u64::from(mem.read_u32(fds + 4).unwrap());
let msg = 0x1_1000;
mem.write_init(msg, b"yo").unwrap();
call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[wfd, msg, 2, 0, 0, 0],
);
let epfd = call(
&mut k,
&mut mem,
&mut v,
Sysno::EpollCreate1,
[0, 0, 0, 0, 0, 0],
);
assert!(epfd >= 3);
let epfd = epfd as u64;
let ev = 0x1_2000;
mem.write_init(ev, &1u32.to_le_bytes()).unwrap(); mem.write_init(ev + 8, &0x1234_5678u64.to_le_bytes())
.unwrap(); assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::EpollCtl,
[epfd, 1, rfd, ev, 0, 0]
),
0
);
let out = 0x1_3000;
let n = call(
&mut k,
&mut mem,
&mut v,
Sysno::EpollPwait,
[epfd, out, 4, 0, 0, 0],
);
assert_eq!(n, 1);
assert_eq!(mem.read_u32(out).unwrap() & 1, 1, "POLLIN reported");
assert_eq!(mem.read_u64(out + 8).unwrap(), 0x1234_5678);
}
#[test]
fn timerfd_create_settime_gettime() {
let (mut k, mut mem, mut v) = setup();
let fd = call(
&mut k,
&mut mem,
&mut v,
Sysno::TimerfdCreate,
[0, 0, 0, 0, 0, 0],
);
assert!(fd >= 3);
let fd = fd as u64;
let newval = 0x1_0000;
mem.write_init(newval, &0u64.to_le_bytes()).unwrap(); mem.write_init(newval + 8, &0u64.to_le_bytes()).unwrap(); mem.write_init(newval + 16, &10u64.to_le_bytes()).unwrap(); mem.write_init(newval + 24, &0u64.to_le_bytes()).unwrap(); assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::TimerfdSettime,
[fd, 0, newval, 0, 0, 0]
),
0
);
let curval = 0x1_1000;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::TimerfdGettime,
[fd, curval, 0, 0, 0, 0]
),
0
);
assert_eq!(mem.read_u64(curval).unwrap(), 0);
assert_eq!(mem.read_u64(curval + 8).unwrap(), 0);
let rem_sec = mem.read_u64(curval + 16).unwrap();
assert!(rem_sec <= 10, "remaining seconds should be at most 10");
let out = 0x1_2000;
let ret = call(&mut k, &mut mem, &mut v, Sysno::Read, [fd, out, 8, 0, 0, 0]);
assert_eq!(ret, 0);
assert!(k.block);
}
#[test]
fn timerfd_disarm_with_zero_value() {
let (mut k, mut mem, mut v) = setup();
let fd = call(
&mut k,
&mut mem,
&mut v,
Sysno::TimerfdCreate,
[0, 0, 0, 0, 0, 0],
) as u64;
let newval = 0x1_0000;
mem.write_init(newval, &[0u8; 32]).unwrap(); assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::TimerfdSettime,
[fd, 0, newval, 0, 0, 0]
),
0
);
let curval = 0x1_1000;
call(
&mut k,
&mut mem,
&mut v,
Sysno::TimerfdGettime,
[fd, curval, 0, 0, 0, 0],
);
assert_eq!(mem.read_u64(curval + 16).unwrap(), 0);
assert_eq!(mem.read_u64(curval + 24).unwrap(), 0);
}
}