// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
//
// Portions Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-Google file.

/* Auto-generated by bindgen then manually edited for simplicity */

#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(missing_docs)]
#![allow(clippy::missing_safety_doc)]

use crate::{Error, Result};
use std::os::raw;

pub const VHOST: raw::c_uint = 0xaf;
pub const VHOST_VRING_F_LOG: raw::c_uint = 0;
pub const VHOST_ACCESS_RO: raw::c_uchar = 1;
pub const VHOST_ACCESS_WO: raw::c_uchar = 2;
pub const VHOST_ACCESS_RW: raw::c_uchar = 3;
pub const VHOST_IOTLB_MISS: raw::c_uchar = 1;
pub const VHOST_IOTLB_UPDATE: raw::c_uchar = 2;
pub const VHOST_IOTLB_INVALIDATE: raw::c_uchar = 3;
pub const VHOST_IOTLB_ACCESS_FAIL: raw::c_uchar = 4;
pub const VHOST_IOTLB_BATCH_BEGIN: raw::c_uchar = 5;
pub const VHOST_IOTLB_BATCH_END: raw::c_uchar = 6;
pub const VHOST_IOTLB_MSG: raw::c_int = 1;
pub const VHOST_IOTLB_MSG_V2: raw::c_uint = 2;
pub const VHOST_PAGE_SIZE: raw::c_uint = 4096;
pub const VHOST_VIRTIO: raw::c_uint = 175;
pub const VHOST_VRING_LITTLE_ENDIAN: raw::c_uint = 0;
pub const VHOST_VRING_BIG_ENDIAN: raw::c_uint = 1;
pub const VHOST_F_LOG_ALL: raw::c_uint = 26;
pub const VHOST_NET_F_VIRTIO_NET_HDR: raw::c_uint = 27;
pub const VHOST_SCSI_ABI_VERSION: raw::c_uint = 1;
pub const VHOST_BACKEND_F_IOTLB_MSG_V2: raw::c_ulonglong = 0x1;
pub const VHOST_BACKEND_F_IOTLB_BATCH: raw::c_ulonglong = 0x2;
pub const VHOST_BACKEND_F_IOTLB_ASID: raw::c_ulonglong = 0x3;
pub const VHOST_BACKEND_F_SUSPEND: raw::c_ulonglong = 0x4;

ioctl_ior_nr!(VHOST_GET_FEATURES, VHOST, 0x00, raw::c_ulonglong);
ioctl_iow_nr!(VHOST_SET_FEATURES, VHOST, 0x00, raw::c_ulonglong);
ioctl_io_nr!(VHOST_SET_OWNER, VHOST, 0x01);
ioctl_io_nr!(VHOST_RESET_OWNER, VHOST, 0x02);
ioctl_iow_nr!(VHOST_SET_MEM_TABLE, VHOST, 0x03, vhost_memory);
ioctl_iow_nr!(VHOST_SET_LOG_BASE, VHOST, 0x04, raw::c_ulonglong);
ioctl_iow_nr!(VHOST_SET_LOG_FD, VHOST, 0x07, raw::c_int);
ioctl_iow_nr!(VHOST_SET_VRING_NUM, VHOST, 0x10, vhost_vring_state);
ioctl_iow_nr!(VHOST_SET_VRING_ADDR, VHOST, 0x11, vhost_vring_addr);
ioctl_iow_nr!(VHOST_SET_VRING_BASE, VHOST, 0x12, vhost_vring_state);
ioctl_iowr_nr!(VHOST_GET_VRING_BASE, VHOST, 0x12, vhost_vring_state);
ioctl_iow_nr!(VHOST_SET_VRING_KICK, VHOST, 0x20, vhost_vring_file);
ioctl_iow_nr!(VHOST_SET_VRING_CALL, VHOST, 0x21, vhost_vring_file);
ioctl_iow_nr!(VHOST_SET_VRING_ERR, VHOST, 0x22, vhost_vring_file);
ioctl_iow_nr!(VHOST_SET_BACKEND_FEATURES, VHOST, 0x25, raw::c_ulonglong);
ioctl_ior_nr!(VHOST_GET_BACKEND_FEATURES, VHOST, 0x26, raw::c_ulonglong);
ioctl_iow_nr!(VHOST_NET_SET_BACKEND, VHOST, 0x30, vhost_vring_file);
ioctl_iow_nr!(VHOST_SCSI_SET_ENDPOINT, VHOST, 0x40, vhost_scsi_target);
ioctl_iow_nr!(VHOST_SCSI_CLEAR_ENDPOINT, VHOST, 0x41, vhost_scsi_target);
ioctl_iow_nr!(VHOST_SCSI_GET_ABI_VERSION, VHOST, 0x42, raw::c_int);
ioctl_iow_nr!(VHOST_SCSI_SET_EVENTS_MISSED, VHOST, 0x43, raw::c_uint);
ioctl_iow_nr!(VHOST_SCSI_GET_EVENTS_MISSED, VHOST, 0x44, raw::c_uint);
ioctl_iow_nr!(VHOST_VSOCK_SET_GUEST_CID, VHOST, 0x60, raw::c_ulonglong);
ioctl_iow_nr!(VHOST_VSOCK_SET_RUNNING, VHOST, 0x61, raw::c_int);
ioctl_ior_nr!(VHOST_VDPA_GET_DEVICE_ID, VHOST, 0x70, raw::c_uint);
ioctl_ior_nr!(VHOST_VDPA_GET_STATUS, VHOST, 0x71, raw::c_uchar);
ioctl_iow_nr!(VHOST_VDPA_SET_STATUS, VHOST, 0x72, raw::c_uchar);
ioctl_ior_nr!(VHOST_VDPA_GET_CONFIG, VHOST, 0x73, vhost_vdpa_config);
ioctl_iow_nr!(VHOST_VDPA_SET_CONFIG, VHOST, 0x74, vhost_vdpa_config);
ioctl_iow_nr!(VHOST_VDPA_SET_VRING_ENABLE, VHOST, 0x75, vhost_vring_state);
ioctl_ior_nr!(VHOST_VDPA_GET_VRING_NUM, VHOST, 0x76, raw::c_ushort);
ioctl_iow_nr!(VHOST_VDPA_SET_CONFIG_CALL, VHOST, 0x77, raw::c_int);
ioctl_ior_nr!(
    VHOST_VDPA_GET_IOVA_RANGE,
    VHOST,
    0x78,
    vhost_vdpa_iova_range
);
ioctl_ior_nr!(VHOST_VDPA_GET_CONFIG_SIZE, VHOST, 0x79, raw::c_uint);
ioctl_ior_nr!(VHOST_VDPA_GET_VQS_COUNT, VHOST, 0x80, raw::c_uint);
ioctl_ior_nr!(VHOST_VDPA_GET_GROUP_NUM, VHOST, 0x81, raw::c_uint);
ioctl_ior_nr!(VHOST_VDPA_GET_AS_NUM, VHOST, 0x7a, raw::c_uint);
ioctl_iowr_nr!(VHOST_VDPA_GET_VRING_GROUP, VHOST, 0x7b, vhost_vring_state);
ioctl_iow_nr!(VHOST_VDPA_SET_GROUP_ASID, VHOST, 0x7c, vhost_vring_state);
ioctl_io_nr!(VHOST_VDPA_SUSPEND, VHOST, 0x7d);

#[repr(C)]
#[derive(Default)]
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);

impl<T> __IncompleteArrayField<T> {
    #[inline]
    pub fn new() -> Self {
        __IncompleteArrayField(::std::marker::PhantomData)
    }

    #[inline]
    #[allow(clippy::trivially_copy_pass_by_ref)]
    #[allow(clippy::useless_transmute)]
    pub unsafe fn as_ptr(&self) -> *const T {
        ::std::mem::transmute(self)
    }

    #[inline]
    #[allow(clippy::useless_transmute)]
    pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
        ::std::mem::transmute(self)
    }

    #[inline]
    pub unsafe fn as_slice(&self, len: usize) -> &[T] {
        ::std::slice::from_raw_parts(self.as_ptr(), len)
    }

    #[inline]
    pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
        ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
    }
}

impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        fmt.write_str("__IncompleteArrayField")
    }
}

impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
    #[inline]
    fn clone(&self) -> Self {
        Self::new()
    }
}

impl<T> ::std::marker::Copy for __IncompleteArrayField<T> {}

#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct vhost_vring_state {
    pub index: raw::c_uint,
    pub num: raw::c_uint,
}

#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct vhost_vring_file {
    pub index: raw::c_uint,
    pub fd: raw::c_int,
}

#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct vhost_vring_addr {
    pub index: raw::c_uint,
    pub flags: raw::c_uint,
    pub desc_user_addr: raw::c_ulonglong,
    pub used_user_addr: raw::c_ulonglong,
    pub avail_user_addr: raw::c_ulonglong,
    pub log_guest_addr: raw::c_ulonglong,
}

#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct vhost_iotlb_msg {
    pub iova: raw::c_ulonglong,
    pub size: raw::c_ulonglong,
    pub uaddr: raw::c_ulonglong,
    pub perm: raw::c_uchar,
    pub type_: raw::c_uchar,
}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct vhost_msg {
    pub type_: raw::c_int,
    pub __bindgen_anon_1: vhost_msg__bindgen_ty_1,
}

impl Default for vhost_msg {
    fn default() -> Self {
        // SAFETY: Zeroing all bytes is fine because they represent a valid
        // value for all members of the structure
        unsafe { ::std::mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Copy, Clone)]
pub union vhost_msg__bindgen_ty_1 {
    pub iotlb: vhost_iotlb_msg,
    pub padding: [raw::c_uchar; 64usize],
    _bindgen_union_align: [u64; 8usize],
}

impl Default for vhost_msg__bindgen_ty_1 {
    fn default() -> Self {
        // SAFETY: Zeroing all bytes is fine because they represent a valid
        // value for all members of the structure
        unsafe { ::std::mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct vhost_msg_v2 {
    pub type_: raw::c_uint,
    pub reserved: raw::c_uint,
    pub __bindgen_anon_1: vhost_msg_v2__bindgen_ty_1,
}

impl Default for vhost_msg_v2 {
    fn default() -> Self {
        // SAFETY: Zeroing all bytes is fine because they represent a valid
        // value for all members of the structure
        unsafe { ::std::mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Copy, Clone)]
pub union vhost_msg_v2__bindgen_ty_1 {
    pub iotlb: vhost_iotlb_msg,
    pub padding: [raw::c_uchar; 64usize],
    _bindgen_union_align: [u64; 8usize],
}

impl Default for vhost_msg_v2__bindgen_ty_1 {
    fn default() -> Self {
        // SAFETY: Zeroing all bytes is fine because they represent a valid
        // value for all members of the structure
        unsafe { ::std::mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct vhost_memory_region {
    pub guest_phys_addr: raw::c_ulonglong,
    pub memory_size: raw::c_ulonglong,
    pub userspace_addr: raw::c_ulonglong,
    pub flags_padding: raw::c_ulonglong,
}

#[repr(C)]
#[derive(Debug, Default, Clone)]
pub struct vhost_memory {
    pub nregions: raw::c_uint,
    pub padding: raw::c_uint,
    pub regions: __IncompleteArrayField<vhost_memory_region>,
    __force_alignment: [u64; 0],
}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct vhost_scsi_target {
    pub abi_version: raw::c_int,
    pub vhost_wwpn: [raw::c_char; 224usize],
    pub vhost_tpgt: raw::c_ushort,
    pub reserved: raw::c_ushort,
}

impl Default for vhost_scsi_target {
    fn default() -> Self {
        // SAFETY: Zeroing all bytes is fine because they represent a valid
        // value for all members of the structure
        unsafe { ::std::mem::zeroed() }
    }
}

#[repr(C)]
#[derive(Debug, Default)]
pub struct vhost_vdpa_config {
    pub off: raw::c_uint,
    pub len: raw::c_uint,
    pub buf: __IncompleteArrayField<raw::c_uchar>,
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct vhost_vdpa_iova_range {
    pub first: raw::c_ulonglong,
    pub last: raw::c_ulonglong,
}

/// Helper to support vhost::set_mem_table()
pub struct VhostMemory {
    buf: Vec<vhost_memory>,
}

impl VhostMemory {
    // Limit number of regions to u16 to simplify error handling
    pub fn new(entries: u16) -> Self {
        let size = std::mem::size_of::<vhost_memory_region>() * entries as usize;
        let count = (size + 2 * std::mem::size_of::<vhost_memory>() - 1)
            / std::mem::size_of::<vhost_memory>();
        let mut buf: Vec<vhost_memory> = vec![Default::default(); count];
        buf[0].nregions = u32::from(entries);
        VhostMemory { buf }
    }

    pub fn as_ptr(&self) -> *const char {
        &self.buf[0] as *const vhost_memory as *const char
    }

    pub fn get_header(&self) -> &vhost_memory {
        &self.buf[0]
    }

    pub fn get_region(&self, index: u32) -> Option<&vhost_memory_region> {
        if index >= self.buf[0].nregions {
            return None;
        }
        // SAFETY: Safe because we have allocated enough space nregions
        let regions = unsafe { self.buf[0].regions.as_slice(self.buf[0].nregions as usize) };
        Some(&regions[index as usize])
    }

    pub fn set_region(&mut self, index: u32, region: &vhost_memory_region) -> Result<()> {
        if index >= self.buf[0].nregions {
            return Err(Error::InvalidGuestMemory);
        }
        // SAFETY: Safe because we have allocated enough space nregions and checked the index.
        let regions = unsafe { self.buf[0].regions.as_mut_slice(index as usize + 1) };
        regions[index as usize] = *region;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn bindgen_test_layout_vhost_vring_state() {
        assert_eq!(
            ::std::mem::size_of::<vhost_vring_state>(),
            8usize,
            concat!("Size of: ", stringify!(vhost_vring_state))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_vring_state>(),
            4usize,
            concat!("Alignment of ", stringify!(vhost_vring_state))
        );
    }

    #[test]
    fn bindgen_test_layout_vhost_vring_file() {
        assert_eq!(
            ::std::mem::size_of::<vhost_vring_file>(),
            8usize,
            concat!("Size of: ", stringify!(vhost_vring_file))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_vring_file>(),
            4usize,
            concat!("Alignment of ", stringify!(vhost_vring_file))
        );
    }

    #[test]
    fn bindgen_test_layout_vhost_vring_addr() {
        assert_eq!(
            ::std::mem::size_of::<vhost_vring_addr>(),
            40usize,
            concat!("Size of: ", stringify!(vhost_vring_addr))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_vring_addr>(),
            8usize,
            concat!("Alignment of ", stringify!(vhost_vring_addr))
        );
    }

    #[test]
    fn bindgen_test_layout_vhost_msg__bindgen_ty_1() {
        assert_eq!(
            ::std::mem::size_of::<vhost_msg__bindgen_ty_1>(),
            64usize,
            concat!("Size of: ", stringify!(vhost_msg__bindgen_ty_1))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_msg__bindgen_ty_1>(),
            8usize,
            concat!("Alignment of ", stringify!(vhost_msg__bindgen_ty_1))
        );
    }

    #[test]
    fn bindgen_test_layout_vhost_msg() {
        assert_eq!(
            ::std::mem::size_of::<vhost_msg>(),
            72usize,
            concat!("Size of: ", stringify!(vhost_msg))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_msg>(),
            8usize,
            concat!("Alignment of ", stringify!(vhost_msg))
        );
    }

    #[test]
    fn bindgen_test_layout_vhost_msg_v2__bindgen_ty_1() {
        assert_eq!(
            ::std::mem::size_of::<vhost_msg_v2__bindgen_ty_1>(),
            64usize,
            concat!("Size of: ", stringify!(vhost_msg_v2__bindgen_ty_1))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_msg_v2__bindgen_ty_1>(),
            8usize,
            concat!("Alignment of ", stringify!(vhost_msg_v2__bindgen_ty_1))
        );
    }

    #[test]
    fn bindgen_test_layout_vhost_msg_v2() {
        assert_eq!(
            ::std::mem::size_of::<vhost_msg_v2>(),
            72usize,
            concat!("Size of: ", stringify!(vhost_msg_v2))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_msg_v2>(),
            8usize,
            concat!("Alignment of ", stringify!(vhost_msg_v2))
        );
    }

    #[test]
    fn bindgen_test_layout_vhost_memory_region() {
        assert_eq!(
            ::std::mem::size_of::<vhost_memory_region>(),
            32usize,
            concat!("Size of: ", stringify!(vhost_memory_region))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_memory_region>(),
            8usize,
            concat!("Alignment of ", stringify!(vhost_memory_region))
        );
    }

    #[test]
    fn bindgen_test_layout_vhost_memory() {
        assert_eq!(
            ::std::mem::size_of::<vhost_memory>(),
            8usize,
            concat!("Size of: ", stringify!(vhost_memory))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_memory>(),
            8usize,
            concat!("Alignment of ", stringify!(vhost_memory))
        );
    }

    #[test]
    fn bindgen_test_layout_vhost_iotlb_msg() {
        assert_eq!(
            ::std::mem::size_of::<vhost_iotlb_msg>(),
            32usize,
            concat!("Size of: ", stringify!(vhost_iotlb_msg))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_iotlb_msg>(),
            8usize,
            concat!("Alignment of ", stringify!(vhost_iotlb_msg))
        );
    }

    #[test]
    fn bindgen_test_layout_vhost_scsi_target() {
        assert_eq!(
            ::std::mem::size_of::<vhost_scsi_target>(),
            232usize,
            concat!("Size of: ", stringify!(vhost_scsi_target))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_scsi_target>(),
            4usize,
            concat!("Alignment of ", stringify!(vhost_scsi_target))
        );
    }

    #[test]
    fn bindgen_test_layout_vhost_vdpa_config() {
        assert_eq!(
            ::std::mem::size_of::<vhost_vdpa_config>(),
            8usize,
            concat!("Size of: ", stringify!(vhost_vdpa_config))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_vdpa_config>(),
            4usize,
            concat!("Alignment of ", stringify!(vhost_vdpa_config))
        );
    }

    #[test]
    fn bindgen_test_layout_vhost_vdpa_iova_range() {
        assert_eq!(
            ::std::mem::size_of::<vhost_vdpa_iova_range>(),
            16usize,
            concat!("Size of: ", stringify!(vhost_vdpa_iova_range))
        );
        assert_eq!(
            ::std::mem::align_of::<vhost_vdpa_iova_range>(),
            8usize,
            concat!("Alignment of ", stringify!(vhost_vdpa_iova_range))
        );
    }

    #[test]
    fn test_vhostmemory() {
        let mut obj = VhostMemory::new(2);
        let region = vhost_memory_region {
            guest_phys_addr: 0x1000u64,
            memory_size: 0x2000u64,
            userspace_addr: 0x300000u64,
            flags_padding: 0u64,
        };
        assert!(obj.get_region(2).is_none());

        {
            let header = obj.get_header();
            assert_eq!(header.nregions, 2u32);
        }
        {
            assert!(obj.set_region(0, &region).is_ok());
            assert!(obj.set_region(1, &region).is_ok());
            assert!(obj.set_region(2, &region).is_err());
        }

        let region1 = obj.get_region(1).unwrap();
        assert_eq!(region1.guest_phys_addr, 0x1000u64);
        assert_eq!(region1.memory_size, 0x2000u64);
        assert_eq!(region1.userspace_addr, 0x300000u64);
    }
}
