diff --git a/src/kernel/src/dev/ttyconsole.rs b/src/kernel/src/dev/ttyconsole.rs index 57895c1c..94c72ce7 100644 --- a/src/kernel/src/dev/ttyconsole.rs +++ b/src/kernel/src/dev/ttyconsole.rs @@ -1,4 +1,4 @@ -use crate::errno::{Errno, EPERM}; +use crate::errno::Errno; use crate::fs::{ make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, IoLen, IoVec, IoVecMut, MakeDevError, MakeDevFlags, Mode, OpenFlags, diff --git a/src/kernel/src/hv/linux/kvm.cpp b/src/kernel/src/hv/linux/kvm.cpp index f1c8a095..28645713 100644 --- a/src/kernel/src/hv/linux/kvm.cpp +++ b/src/kernel/src/hv/linux/kvm.cpp @@ -67,6 +67,11 @@ extern "C" int kvm_set_user_memory_region( return 0; } +extern "C" int kvm_get_vcpu_mmap_size(int kvm) +{ + return ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE); +} + extern "C" int kvm_create_vcpu(int vm, int id, int *fd) { auto vcpu = ioctl(vm, KVM_CREATE_VCPU, id); @@ -79,3 +84,18 @@ extern "C" int kvm_create_vcpu(int vm, int id, int *fd) return 0; } + +extern "C" int kvm_run(int vcpu) +{ + return ioctl(vcpu, KVM_RUN); +} + +extern "C" int kvm_get_regs(int vcpu, kvm_regs *regs) +{ + return ioctl(vcpu, KVM_GET_REGS, regs); +} + +extern "C" int kvm_set_regs(int vcpu, kvm_regs *const regs) +{ + return ioctl(vcpu, KVM_SET_REGS, regs); +} diff --git a/src/kernel/src/hv/linux/mod.rs b/src/kernel/src/hv/linux/mod.rs index a6bce939..94fc6552 100644 --- a/src/kernel/src/hv/linux/mod.rs +++ b/src/kernel/src/hv/linux/mod.rs @@ -1,74 +1,186 @@ +use self::regs::KvmRegs; +use self::run::KvmRun; use super::HypervisorError; -use libc::{open, O_RDWR}; use std::ffi::{c_int, c_void}; use std::io::Error; -use std::os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd}; +use std::mem::MaybeUninit; +use std::os::fd::{AsRawFd, FromRawFd, OwnedFd}; +use std::ptr::NonNull; +use thiserror::Error; -pub fn open_kvm() -> Result { - // Open KVM. - let fd = unsafe { open(c"/dev/kvm".as_ptr(), O_RDWR) }; +mod regs; +mod run; - if fd < 0 { - return Err(HypervisorError::OpenKvmFailed(Error::last_os_error())); - } +pub struct Kvm(OwnedFd); - // Check KVM version. - let fd = unsafe { OwnedFd::from_raw_fd(fd) }; - let mut compat = false; +impl Kvm { + pub fn open() -> Result { + use libc::{open, O_RDWR}; - match unsafe { kvm_check_version(fd.as_raw_fd(), &mut compat) } { - 0 => { - if !compat { + let fd = unsafe { open(c"/dev/kvm".as_ptr(), O_RDWR) }; + + if fd < 0 { + return Err(HypervisorError::OpenKvmFailed(Error::last_os_error())); + } + + // Check KVM version. + let fd = unsafe { OwnedFd::from_raw_fd(fd) }; + let mut compat = false; + + match unsafe { kvm_check_version(fd.as_raw_fd(), &mut compat) } { + 0 if !compat => { return Err(HypervisorError::KvmVersionMismatched); } + 0 => {} + v => { + return Err(HypervisorError::GetKvmVersionFailed( + Error::from_raw_os_error(v), + )) + } } - v => { - return Err(HypervisorError::GetKvmVersionFailed( - Error::from_raw_os_error(v), - )) + + Ok(Self(fd)) + } + + pub fn get_vcpu_mmap_size(&self) -> Result { + match unsafe { kvm_get_vcpu_mmap_size(self.0.as_raw_fd()) } { + size @ 0.. => Ok(size as usize), + _ => Err(HypervisorError::GetMmapSizeFailed(Error::last_os_error())), } } - Ok(fd) -} + pub fn max_vcpus(&self) -> Result { + let mut max = 0; -pub fn max_vcpus(kvm: BorrowedFd) -> Result { - let mut max = 0; + match unsafe { kvm_max_vcpus(self.0.as_raw_fd(), &mut max) } { + 0 => Ok(max), + v => Err(HypervisorError::GetMaxCpuFailed(Error::from_raw_os_error( + v, + ))), + } + } - match unsafe { kvm_max_vcpus(kvm.as_raw_fd(), &mut max) } { - 0 => Ok(max), - v => Err(Error::from_raw_os_error(v)), + pub fn create_vm(&self) -> Result { + let mut vm = -1; + + match unsafe { kvm_create_vm(self.0.as_raw_fd(), &mut vm) } { + 0 => Ok(Vm(unsafe { OwnedFd::from_raw_fd(vm) })), + v => Err(HypervisorError::CreateVmFailed(Error::from_raw_os_error(v))), + } } } -pub fn create_vm(kvm: BorrowedFd) -> Result { - let mut vm = -1; +pub struct Vm(OwnedFd); - match unsafe { kvm_create_vm(kvm.as_raw_fd(), &mut vm) } { - 0 => Ok(unsafe { OwnedFd::from_raw_fd(vm) }), - v => Err(Error::from_raw_os_error(v)), +impl Vm { + pub fn set_user_memory_region( + &self, + slot: u32, + addr: u64, + len: u64, + mem: *mut c_void, + ) -> Result<(), HypervisorError> { + match unsafe { kvm_set_user_memory_region(self.0.as_raw_fd(), slot, addr, len, mem) } { + 0 => Ok(()), + v => Err(HypervisorError::MapRamFailed(Error::from_raw_os_error(v))), + } + } + + pub fn create_vcpus(&self, mmap_size: usize) -> Result { + let vcpus = [ + self.create_vcpu(0, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 0))?, + self.create_vcpu(1, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 1))?, + self.create_vcpu(2, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 2))?, + self.create_vcpu(3, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 3))?, + self.create_vcpu(4, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 4))?, + self.create_vcpu(5, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 5))?, + self.create_vcpu(6, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 6))?, + self.create_vcpu(7, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 7))?, + ]; + + Ok(VCpus(vcpus)) + } + + fn create_vcpu(&self, id: i32, mmap_size: usize) -> Result { + use libc::{mmap, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE}; + + let mut vcpu = -1; + + let fd = match unsafe { kvm_create_vcpu(self.0.as_raw_fd(), id, &mut vcpu) } { + 0 => Ok(unsafe { OwnedFd::from_raw_fd(vcpu) }), + v => Err(CreateVCpuError::CreateVcpuFailed(Error::from_raw_os_error( + v, + ))), + }?; + + let kvm_run = unsafe { + mmap( + std::ptr::null_mut(), + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd.as_raw_fd(), + 0, + ) + }; + + if kvm_run == MAP_FAILED { + return Err(CreateVCpuError::MmapFailed(Error::last_os_error())); + } + + Ok(VCpu { + fd, + kvm_run: NonNull::new(kvm_run.cast()).unwrap(), + mmap_size, + }) } } -pub fn set_user_memory_region( - vm: BorrowedFd, - slot: u32, - addr: u64, - len: u64, - mem: *mut c_void, -) -> Result<(), Error> { - match unsafe { kvm_set_user_memory_region(vm.as_raw_fd(), slot, addr, len, mem) } { - 0 => Ok(()), - v => Err(Error::from_raw_os_error(v)), +#[derive(Debug)] +pub struct VCpus([VCpu; 8]); + +#[derive(Debug)] +struct VCpu { + fd: OwnedFd, + kvm_run: NonNull, + mmap_size: usize, +} + +impl Drop for VCpu { + fn drop(&mut self) { + use libc::munmap; + + unsafe { + if munmap(self.kvm_run.as_ptr().cast(), self.mmap_size) < 0 { + panic!("failed to munmap KVM_RUN: {}", Error::last_os_error()); + }; + } } } -pub fn create_vcpu(vm: BorrowedFd, id: i32) -> Result { - let mut vcpu = -1; +impl VCpu { + pub fn get_regs(&self) -> Result { + let mut regs = MaybeUninit::uninit(); - match unsafe { kvm_create_vcpu(vm.as_raw_fd(), id, &mut vcpu) } { - 0 => Ok(unsafe { OwnedFd::from_raw_fd(vcpu) }), - v => Err(Error::from_raw_os_error(v)), + match unsafe { kvm_get_regs(self.fd.as_raw_fd(), regs.as_mut_ptr()) } { + 0 => Ok(unsafe { regs.assume_init() }), + _ => Err(Error::last_os_error()), + } + } + + pub fn set_regs(&self, regs: KvmRegs) -> Result<(), Error> { + match unsafe { kvm_set_regs(self.fd.as_raw_fd(), ®s) } { + 0 => Ok(()), + _ => Err(Error::last_os_error()), + } } } @@ -76,6 +188,8 @@ extern "C" { fn kvm_check_version(kvm: c_int, compat: *mut bool) -> c_int; fn kvm_max_vcpus(kvm: c_int, max: *mut usize) -> c_int; fn kvm_create_vm(kvm: c_int, fd: *mut c_int) -> c_int; + fn kvm_get_vcpu_mmap_size(kvm: c_int) -> c_int; + fn kvm_set_user_memory_region( vm: c_int, slot: u32, @@ -84,4 +198,23 @@ extern "C" { mem: *mut c_void, ) -> c_int; fn kvm_create_vcpu(vm: c_int, id: c_int, fd: *mut c_int) -> c_int; + + fn kvm_run(vcpu: c_int) -> c_int; + fn kvm_get_regs(vcpu: c_int, regs: *mut KvmRegs) -> c_int; + fn kvm_set_regs(vcpu: c_int, regs: *const KvmRegs) -> c_int; +} + +#[derive(Debug, Error)] +pub enum CreateVCpusError { + #[error("failed to create vcpu #{1}")] + CreateVcpuFailed(#[source] CreateVCpuError, u8), +} + +#[derive(Debug, Error)] +pub enum CreateVCpuError { + #[error("failed to create vcpu")] + CreateVcpuFailed(#[source] Error), + + #[error("failed to mmap KVM_RUN")] + MmapFailed(#[source] Error), } diff --git a/src/kernel/src/hv/linux/regs.rs b/src/kernel/src/hv/linux/regs.rs new file mode 100644 index 00000000..b9bb280f --- /dev/null +++ b/src/kernel/src/hv/linux/regs.rs @@ -0,0 +1,75 @@ +#[repr(C)] +pub struct KvmRegs { + rax: u64, + rbx: u64, + rcx: u64, + rdx: u64, + + rsi: u64, + rdi: u64, + rsp: u64, + rbp: u64, + + r8: u64, + r9: u64, + r10: u64, + r11: u64, + + r12: u64, + r13: u64, + r14: u64, + r15: u64, + + rip: u64, + rflags: u64, +} + +#[repr(C)] +pub struct KvmSpecialRegs { + cs: KvmSegment, + ds: KvmSegment, + es: KvmSegment, + fs: KvmSegment, + gs: KvmSegment, + ss: KvmSegment, + + tr: KvmSegment, + ldt: KvmSegment, + + gdt: KvmDTable, + idt: KvmDTable, + + cr0: u64, + cr2: u64, + cr3: u64, + cr4: u64, + cr8: u64, + + efer: u64, + apic_base: u64, + interrupt_bitmap: [u64; 4], +} + +#[repr(C)] +struct KvmSegment { + base: u64, + limit: u32, + selector: u16, + ty: u8, + present: u8, + dpl: u8, + db: u8, + s: u8, + l: u8, + g: u8, + avl: u8, + unusable: u8, + padding: u8, +} + +#[repr(C)] +struct KvmDTable { + base: u64, + limit: u16, + padding: [u16; 3], +} diff --git a/src/kernel/src/hv/linux/run.rs b/src/kernel/src/hv/linux/run.rs new file mode 100644 index 00000000..28a8e70a --- /dev/null +++ b/src/kernel/src/hv/linux/run.rs @@ -0,0 +1,307 @@ +use std::mem::ManuallyDrop; + +#[repr(C)] +pub struct KvmRun { + request_interrupt_window: u8, + immediate_exit: u8, + padding1: [u8; 6], + + exit_reason: u32, + ready_for_interrupt_injection: u8, + if_flag: u8, + flags: u16, + + cr8: u64, + apic_base: u64, + exit: Exit, +} + +#[repr(C)] +union Exit { + hw: ManuallyDrop, + fail_entry: ManuallyDrop, + ex: ManuallyDrop, + io: ManuallyDrop, + debug: ManuallyDrop, + mmio: ManuallyDrop, + iocsr_io: ManuallyDrop, + hypercall: ManuallyDrop, + tpr_access: ManuallyDrop, + s390_sieic: ManuallyDrop, + s390_reset_flags: u64, + s390_ucontrol: ManuallyDrop, + dcr: ManuallyDrop, + internal: ManuallyDrop, + emulation_failure: ManuallyDrop, + osi: ManuallyDrop, + papr_hcall: ManuallyDrop, + s390_tsch: ManuallyDrop, + epr: ManuallyDrop, + system_event: ManuallyDrop, + s390_stsi: ManuallyDrop, + eoi: ManuallyDrop, + hyperv: ManuallyDrop, + arm_nisv: ManuallyDrop, + msr: ManuallyDrop, + xen: ManuallyDrop, + riscv_sbi: ManuallyDrop, + riscv_csr: ManuallyDrop, + notify: ManuallyDrop, + padding: [u8; 256], +} + +#[repr(C)] +struct Hw { + hardware_exit_reason: u64, +} + +#[repr(C)] +struct FailEntry { + hardware_entry_failure_reason: u64, + cpu: u32, +} + +#[repr(C)] +struct Ex { + exception: u32, + error_code: u32, +} + +#[repr(C)] +struct Io { + direction: u8, + size: u8, + port: u16, + count: u32, + data_offset: u64, +} + +#[repr(C)] +struct Debug { + arch: KvmDebugExitArch, +} + +#[repr(C)] +struct KvmDebugExitArch { + exception: u32, + pad: u32, + pc: u64, + dr6: u64, + dr7: u64, +} + +#[repr(C)] +struct Mmio { + phys_addr: u64, + data: [u8; 8], + len: u32, + is_write: u8, +} + +#[repr(C)] +struct IocsrIo { + phys_addr: u64, + data: [u8; 8], + len: u32, + is_write: u8, +} + +#[repr(C)] +struct Hypercall { + nr: u64, + args: [u64; 6], + ret: u64, + inner: HypercallInner, +} + +/// This struct has to be named in Rust +#[repr(C)] +union HypercallInner { + longmode: u32, + flags: u64, +} + +#[repr(C)] +struct TprAccess { + rip: u64, + is_write: u32, + pad: u32, +} + +#[repr(C)] +struct S390Sieic { + iptcode: u32, + ipa: u16, + ipb: u32, +} + +#[repr(C)] +struct S390Ucontrol { + trans_exc_code: u64, + pgm_code: u32, +} + +#[repr(C)] +struct Dcr { + dcrn: u32, + data: u32, + is_write: u8, +} + +#[repr(C)] +struct Internal { + suberror: u32, + ndata: u32, + data: [u64; 16], +} + +#[repr(C)] +struct EmulationFailure { + suberror: u32, + ndata: u32, + flags: u64, + insn_size: u8, + insn_bytes: [u8; 15], +} + +#[repr(C)] +struct Osi { + gprs: [u64; 32], +} + +#[repr(C)] +struct PaprHcall { + nr: u64, + ret: u64, + args: [u64; 9], +} + +#[repr(C)] +struct S390Tsch { + subchannel_id: u16, + subchannel_nr: u16, + io_int_parm: u32, + io_int_word: u32, + dequeued: u8, +} + +#[repr(C)] +struct Epr { + epr: u32, +} + +#[repr(C)] +struct SystemEvent { + ty: u32, + ndata: u32, + inner: SystemEventInner, +} + +/// This struct has to have a name in Rust +#[repr(C)] +union SystemEventInner { + flags: u64, + data: [u64; 16], +} + +#[repr(C)] +struct S390Stsi { + addr: u64, + ar: u8, + reserver: u8, + fc: u8, + sel1: u8, + sel2: u8, +} + +#[repr(C)] +struct Eoi { + vector: u8, +} + +#[repr(C)] +struct KvmHypervExit { + ty: u32, + pad1: u32, + u: KvmHypervExitInner, +} + +#[repr(C)] +union KvmHypervExitInner { + synic: ManuallyDrop, + hcall: ManuallyDrop, + debug: ManuallyDrop, +} + +#[repr(C)] +struct Synic { + msr: u32, + pad2: u32, + control: u64, + evt_page: u64, + msg_page: u64, +} + +#[repr(C)] +struct Hcall { + input: u64, + result: u64, + params: [u64; 2], +} + +#[repr(C)] +struct Syndbg { + msr: u32, + pad2: u32, + control: u64, + status: u64, + send_page: u64, + recv_page: u64, + pending_page: u64, +} + +#[repr(C)] +struct ArmNisv { + esr_iss: u64, + fault_ipa: u64, +} + +#[repr(C)] +struct Msr { + error: u8, + pad: [u8; 7], + reason: u32, + index: u32, + data: u64, +} + +#[repr(C)] +struct KvmXenExit { + ty: u32, + longmode: u32, + cp1: u32, + input: u64, + result: u64, + params: [u64; 6], +} + +#[repr(C)] +struct RiscvSbi { + extension_id: u64, + function_id: u64, + args: [u64; 6], + ret: [u64; 2], +} + +#[repr(C)] +struct RiscvCsr { + csr_num: u64, + new_value: u64, + write_mask: u64, + ret_value: u64, +} + +#[repr(C)] +struct Notify { + flags: u32, +} diff --git a/src/kernel/src/hv/mod.rs b/src/kernel/src/hv/mod.rs index 3bf7ca97..0ea1ac3d 100644 --- a/src/kernel/src/hv/mod.rs +++ b/src/kernel/src/hv/mod.rs @@ -14,11 +14,11 @@ mod win32; /// Do not create more than one Hypervisor because it will not work on macOS. pub struct Hypervisor { #[cfg(target_os = "linux")] - vcpus: [std::os::fd::OwnedFd; 8], // Drop before VM. + vcpus: self::linux::VCpus, // Drop before VM. #[cfg(target_os = "linux")] - vm: std::os::fd::OwnedFd, // Drop before KVM. + vm: self::linux::Vm, // Drop before KVM. #[cfg(target_os = "linux")] - kvm: std::os::fd::OwnedFd, + kvm: self::linux::Kvm, #[cfg(target_os = "windows")] part: self::win32::Partition, @@ -46,45 +46,27 @@ impl Hypervisor { #[cfg(target_os = "linux")] fn new_linux(ram: Ram) -> Result { - use std::os::fd::AsFd; - // Open KVM device. - let kvm = self::linux::open_kvm()?; + let kvm = self::linux::Kvm::open()?; - if self::linux::max_vcpus(kvm.as_fd()).map_err(HypervisorError::GetMaxCpuFailed)? < 8 { + if kvm.max_vcpus()? < 8 { return Err(HypervisorError::MaxCpuTooLow); } // Create a new VM. - let vm = self::linux::create_vm(kvm.as_fd()).map_err(HypervisorError::CreateVmFailed)?; + let vm = kvm.create_vm()?; - self::linux::set_user_memory_region( - vm.as_fd(), + vm.set_user_memory_region( 0, ram.vm_addr().try_into().unwrap(), ram.len().try_into().unwrap(), ram.host_addr().cast(), - ) - .map_err(HypervisorError::MapRamFailed)?; + )?; - let vcpus = [ - self::linux::create_vcpu(vm.as_fd(), 0) - .map_err(|e| HypervisorError::CreateVcpuFailed(e, 0))?, - self::linux::create_vcpu(vm.as_fd(), 1) - .map_err(|e| HypervisorError::CreateVcpuFailed(e, 1))?, - self::linux::create_vcpu(vm.as_fd(), 2) - .map_err(|e| HypervisorError::CreateVcpuFailed(e, 2))?, - self::linux::create_vcpu(vm.as_fd(), 3) - .map_err(|e| HypervisorError::CreateVcpuFailed(e, 3))?, - self::linux::create_vcpu(vm.as_fd(), 4) - .map_err(|e| HypervisorError::CreateVcpuFailed(e, 4))?, - self::linux::create_vcpu(vm.as_fd(), 5) - .map_err(|e| HypervisorError::CreateVcpuFailed(e, 5))?, - self::linux::create_vcpu(vm.as_fd(), 6) - .map_err(|e| HypervisorError::CreateVcpuFailed(e, 6))?, - self::linux::create_vcpu(vm.as_fd(), 7) - .map_err(|e| HypervisorError::CreateVcpuFailed(e, 7))?, - ]; + let mmap_size = kvm.get_vcpu_mmap_size()?; + let vcpus = vm + .create_vcpus(mmap_size) + .map_err(HypervisorError::CreateVCpusError)?; Ok(Self { vcpus, @@ -155,6 +137,10 @@ pub enum HypervisorError { #[error("couldn't create a RAM")] CreateRamFailed(#[source] std::io::Error), + #[cfg(target_os = "linux")] + #[error("couldn't get maximum number of CPU for a VM")] + GetMaxCpuFailed(#[source] std::io::Error), + #[error("your OS does not support 8 vCPU on a VM")] MaxCpuTooLow, @@ -170,10 +156,6 @@ pub enum HypervisorError { #[error("unexpected KVM version")] KvmVersionMismatched, - #[cfg(target_os = "linux")] - #[error("couldn't get maximum number of CPU for a VM")] - GetMaxCpuFailed(#[source] std::io::Error), - #[cfg(target_os = "linux")] #[error("couldn't create a VM")] CreateVmFailed(#[source] std::io::Error), @@ -183,8 +165,12 @@ pub enum HypervisorError { MapRamFailed(#[source] std::io::Error), #[cfg(target_os = "linux")] - #[error("couldn't create vcpu #{1}")] - CreateVcpuFailed(#[source] std::io::Error, u8), + #[error("couldn't get the size of vCPU mmap")] + GetMmapSizeFailed(#[source] std::io::Error), + + #[cfg(target_os = "linux")] + #[error("couldn't create vCPUs")] + CreateVCpusError(#[source] self::linux::CreateVCpusError), #[cfg(target_os = "windows")] #[error("couldn't create WHP partition object ({0:#x})")] diff --git a/src/kernel/src/hv/ram.rs b/src/kernel/src/hv/ram.rs index 34628a8c..dc262f5e 100644 --- a/src/kernel/src/hv/ram.rs +++ b/src/kernel/src/hv/ram.rs @@ -16,7 +16,6 @@ impl Ram { pub const SIZE: usize = 1024 * 1024 * 1024 * 8; // 8GB pub fn new(addr: usize) -> Result { - // Reserve a memory range on *nix. #[cfg(unix)] let mem = { use libc::{mmap, MAP_ANON, MAP_FAILED, MAP_PRIVATE, PROT_NONE}; @@ -40,7 +39,6 @@ impl Ram { mem.cast() }; - // Reserve a memory range on Windows. #[cfg(windows)] let mem = { use std::ptr::null; diff --git a/src/kernel/src/net/socket.rs b/src/kernel/src/net/socket.rs index cad64d8d..a921927c 100644 --- a/src/kernel/src/net/socket.rs +++ b/src/kernel/src/net/socket.rs @@ -150,7 +150,7 @@ impl FileBackend for SocketFileBackend { IoCmd::SIOCSPGRP(_) => todo!("socket ioctl with SIOCSPGRP"), IoCmd::SIOCGPGRP(_) => todo!("socket ioctl with SIOCGPGRP"), IoCmd::SIOCATMARK(_) => todo!("socket ioctl with SIOCATMARK"), - _ => self.0.backend.control(todo!(), cmd, td), + _ => self.0.backend.control(&self.0, cmd, td), } }