// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Support for individual hypercalls.

use super::support::HypercallDispatch;
use super::support::HypercallParameters;
use super::support::RepHypercall;
use super::support::SimpleHypercall;
use super::support::VariableHypercall;
use super::support::VtlHypercall;
use crate::support::VariableRepHypercall;
use hvdef::hypercall as defs;
use hvdef::hypercall::AcceptPagesAttributes;
use hvdef::hypercall::HostVisibilityType;
use hvdef::hypercall::HvRegisterAssoc;
use hvdef::hypercall::VtlPermissionSet;
use hvdef::HvError;
use hvdef::HvMessage;
use hvdef::HvRegisterName;
use hvdef::HvRegisterValue;
use hvdef::HvRepResult;
use hvdef::HvResult;
use hvdef::HypercallCode;
use hvdef::Vtl;
use zerocopy::AsBytes;

trait MapToHypercallResult {
    fn map_to_hypercall_result(
        &self,
        rep_count: usize,
        elements_processed: &mut usize,
    ) -> HvResult<()>;
}

impl MapToHypercallResult for HvRepResult {
    fn map_to_hypercall_result(
        &self,
        rep_count: usize,
        elements_processed: &mut usize,
    ) -> HvResult<()> {
        match self {
            Ok(()) => {
                *elements_processed = rep_count;
                Ok(())
            }
            Err((e, reps)) => {
                *elements_processed = *reps;
                Err(*e)
            }
        }
    }
}

/// Implements the `HvPostMessage` hypercall.
pub trait PostMessage {
    /// Post a synic message.
    fn post_message(&mut self, connection_id: u32, message: &[u8]) -> HvResult<()>;
}

/// Defines the `HvPostMessage` hypercall.
pub type HvPostMessage =
    SimpleHypercall<defs::PostMessage, (), { HypercallCode::HvCallPostMessage.0 }>;

impl<T: PostMessage> HypercallDispatch<HvPostMessage> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, _) = HvPostMessage::parse(params);
        self.post_message(
            input.connection_id,
            input
                .payload
                .as_bytes()
                .get(..input.payload_size as usize)
                .ok_or(HvError::InvalidParameter)?,
        )
    }
}

/// Implements the `HvSignalEvent` hypercall.
pub trait SignalEvent {
    /// Signal synic event.
    fn signal_event(&mut self, connection_id: u32, flag: u16) -> HvResult<()>;
}

/// Defines the `HvSignalEvent` hypercall.
pub type HvSignalEvent =
    SimpleHypercall<defs::SignalEvent, (), { HypercallCode::HvCallSignalEvent.0 }>;

impl<T: SignalEvent> HypercallDispatch<HvSignalEvent> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, _) = HvSignalEvent::parse(params);
        self.signal_event(input.connection_id, input.flag_number)
    }
}

/// Implements the `HvPostMessageDirect` hypercall.
pub trait PostMessageDirect {
    /// Posts a message directly, without going through a port/connection.
    fn post_message_direct(
        &mut self,
        partition_id: u64,
        target_vtl: Vtl,
        vp: u32,
        sint: u8,
        message: &HvMessage,
    ) -> HvResult<()>;
}

/// Defines the `HvPostMessageDirect` hypercall.
pub type HvPostMessageDirect =
    SimpleHypercall<defs::PostMessageDirect, (), { HypercallCode::HvCallPostMessageDirect.0 }>;

impl<T: PostMessageDirect> HypercallDispatch<HvPostMessageDirect> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, _) = HvPostMessageDirect::parse(params);
        let msg = input.message;
        self.post_message_direct(
            input.partition_id,
            Vtl::try_from(input.vtl)?,
            input.vp_index,
            input.sint,
            &msg,
        )
    }
}

/// Implements the `HvSignalEventDirect` hypercall.
pub trait SignalEventDirect {
    /// Signal synic event directly, without going through a port/connection.
    fn signal_event_direct(
        &mut self,
        partition_id: u64,
        vtl: Vtl,
        vp: u32,
        sint: u8,
        flag: u16,
    ) -> HvResult<defs::SignalEventDirectOutput>;
}

/// Defines the `HvSignalEventDirect` hypercall.
pub type HvSignalEventDirect = SimpleHypercall<
    defs::SignalEventDirect,
    defs::SignalEventDirectOutput,
    { HypercallCode::HvCallSignalEventDirect.0 },
>;

impl<T: SignalEventDirect> HypercallDispatch<HvSignalEventDirect> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, output) = HvSignalEventDirect::parse(params);
        *output = self.signal_event_direct(
            input.target_partition,
            Vtl::try_from(input.target_vtl)?,
            input.target_vp,
            input.target_sint,
            input.flag_number,
        )?;
        Ok(())
    }
}

/// Implements the `HvRetargetDeviceInterrupt` hypercall.
pub trait RetargetDeviceInterrupt {
    /// Retargets a device interrupt to a new processor set.
    fn retarget_interrupt(
        &mut self,
        device_id: u64,
        address: u64,
        data: u32,
        params: &HvInterruptParameters<'_>,
    ) -> HvResult<()>;
}

/// Configuration for a hypervisor device interrupt.
pub struct HvInterruptParameters<'a> {
    /// The target interrupt vector.
    pub vector: u32,
    /// Whether this is a multicast interrupt.
    pub multicast: bool,
    /// A target processor list.
    pub target_processors: &'a [u32],
}

/// Defines the `HvRetargetDeviceInterrupt` hypercall.
pub type HvRetargetDeviceInterrupt = VariableHypercall<
    defs::RetargetDeviceInterrupt,
    (),
    { HypercallCode::HvCallRetargetDeviceInterrupt.0 },
>;

fn parse_processor_masks(mut valid_masks: u64, masks: &[u64]) -> Option<Vec<u32>> {
    let mut procs = Vec::new();
    while valid_masks != 0 {
        let bank = valid_masks.trailing_zeros();
        valid_masks &= !(1 << bank);
        let mut mask = *masks.get(bank as usize)?;
        while mask != 0 {
            let index = mask.trailing_zeros();
            mask &= !(1 << index);
            procs.push(bank * 64 + index);
        }
    }
    Some(procs)
}

fn parse_generic_set(format: u64, rest: &[u64]) -> Option<Vec<u32>> {
    if format != defs::HV_GENERIC_SET_SPARSE_4K {
        return None;
    }
    let &[valid_masks, ref masks @ ..] = rest else {
        return None;
    };
    parse_processor_masks(valid_masks, masks)
}

impl<T: RetargetDeviceInterrupt> HypercallDispatch<HvRetargetDeviceInterrupt> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, var_input, _) = HvRetargetDeviceInterrupt::parse(params);

        if input.target_header.flags.reserved() != 0 {
            return Err(HvError::InvalidParameter);
        }

        let processors = if input.target_header.flags.processor_set() {
            parse_generic_set(input.target_header.mask_or_format, var_input)
        } else {
            parse_processor_masks(1, &[input.target_header.mask_or_format])
        };

        let processors = processors.ok_or(HvError::InvalidParameter)?;

        if input.entry.source != defs::HvInterruptSource::MSI {
            return Err(HvError::InvalidParameter);
        }

        self.retarget_interrupt(
            input.device_id,
            input.entry.data[0] as u64,
            input.entry.data[1],
            &HvInterruptParameters {
                vector: input.target_header.vector,
                multicast: input.target_header.flags.multicast(),
                target_processors: &processors,
            },
        )
    }
}

/// Defines the `HvAssertVirtualInterrupt` hypercall.
pub type HvAssertVirtualInterrupt = SimpleHypercall<
    defs::AssertVirtualInterrupt,
    (),
    { HypercallCode::HvCallAssertVirtualInterrupt.0 },
>;

/// Implements the `HvAssertVirtualInterrupt` hypercall.
pub trait AssertVirtualInterrupt {
    /// Asserts a virtual interrupt.
    fn assert_virtual_interrupt(
        &mut self,
        partition_id: u64,
        interrupt_control: hvdef::HvInterruptControl,
        destination_address: u64,
        requested_vector: u32,
        target_vtl: Vtl,
    ) -> HvResult<()>;
}

impl<T: AssertVirtualInterrupt> HypercallDispatch<HvAssertVirtualInterrupt> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, _) = HvAssertVirtualInterrupt::parse(params);
        self.assert_virtual_interrupt(
            input.partition_id,
            input.interrupt_control,
            input.destination_address,
            input.requested_vector,
            input.target_vtl.try_into()?,
        )
    }
}

/// Defines the `HvStartVirtualProcessor` hypercall for x64.
pub type HvX64StartVirtualProcessor = SimpleHypercall<
    defs::StartVirtualProcessorX64,
    (),
    { HypercallCode::HvCallStartVirtualProcessor.0 },
>;

/// Implements the `HvStartVirtualProcessor` hypercall.
pub trait StartVirtualProcessor<T> {
    /// Starts a virtual processor.
    fn start_virtual_processor(
        &mut self,
        partition_id: u64,
        vp_index: u32,
        target_vtl: Vtl,
        vp_context: &T,
    ) -> HvResult<()>;
}

impl<T: StartVirtualProcessor<defs::InitialVpContextX64>>
    HypercallDispatch<HvX64StartVirtualProcessor> for T
{
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, _) = HvX64StartVirtualProcessor::parse(params);
        self.start_virtual_processor(
            input.partition_id,
            input.vp_index,
            Vtl::try_from(input.target_vtl)?,
            &input.vp_context,
        )
    }
}

/// Defines the `HvStartVirtualProcessor` hypercall for arm64.
pub type HvArm64StartVirtualProcessor = SimpleHypercall<
    defs::StartVirtualProcessorArm64,
    (),
    { HypercallCode::HvCallStartVirtualProcessor.0 },
>;

impl<T: StartVirtualProcessor<defs::InitialVpContextArm64>>
    HypercallDispatch<HvArm64StartVirtualProcessor> for T
{
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, _) = HvArm64StartVirtualProcessor::parse(params);
        self.start_virtual_processor(
            input.partition_id,
            input.vp_index,
            Vtl::try_from(input.target_vtl)?,
            &input.vp_context,
        )
    }
}

/// Defines the `HvTranslateVirtualAddress` hypercall.
pub type HvX64TranslateVirtualAddress = SimpleHypercall<
    defs::TranslateVirtualAddressX64,
    defs::TranslateVirtualAddressOutput,
    { HypercallCode::HvCallTranslateVirtualAddress.0 },
>;

/// Implements the `HvTranslateVirtualAddress` hypercall.
pub trait TranslateVirtualAddressX64 {
    /// Translates a GVA to a GPA.
    fn translate_virtual_address(
        &mut self,
        partition_id: u64,
        vp_index: u32,
        control_flags: defs::TranslateGvaControlFlagsX64,
        gva_page: u64,
    ) -> HvResult<defs::TranslateVirtualAddressOutput>;
}

impl<T: TranslateVirtualAddressX64> HypercallDispatch<HvX64TranslateVirtualAddress> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, output) = HvX64TranslateVirtualAddress::parse(params);

        let control_flags = input.control_flags;
        *output = self.translate_virtual_address(
            input.partition_id,
            input.vp_index,
            control_flags,
            input.gva_page,
        )?;

        Ok(())
    }
}

/// Defines the `HvTranslateVirtualAddressEx` hypercall.
pub type HvX64TranslateVirtualAddressEx = SimpleHypercall<
    defs::TranslateVirtualAddressX64,
    defs::TranslateVirtualAddressExOutputX64,
    { HypercallCode::HvCallTranslateVirtualAddressEx.0 },
>;

/// Implements the `HvTranslateVirtualAddressEx` hypercall.
pub trait TranslateVirtualAddressExX64 {
    /// Translates a GVA to a GPA.
    fn translate_virtual_address_ex(
        &mut self,
        partition_id: u64,
        vp_index: u32,
        control_flags: defs::TranslateGvaControlFlagsX64,
        gva_page: u64,
    ) -> HvResult<defs::TranslateVirtualAddressExOutputX64>;
}

impl<T: TranslateVirtualAddressExX64> HypercallDispatch<HvX64TranslateVirtualAddressEx> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, output) = HvX64TranslateVirtualAddressEx::parse(params);

        let control_flags = input.control_flags;
        *output = self.translate_virtual_address_ex(
            input.partition_id,
            input.vp_index,
            control_flags,
            input.gva_page,
        )?;

        Ok(())
    }
}

/// Defines the `HvTranslateVirtualAddressEx` hypercall.
pub type HvAarch64TranslateVirtualAddressEx = SimpleHypercall<
    defs::TranslateVirtualAddressArm64,
    defs::TranslateVirtualAddressExOutputArm64,
    { HypercallCode::HvCallTranslateVirtualAddressEx.0 },
>;

/// Implements the `HvTranslateVirtualAddressEx` hypercall.
pub trait TranslateVirtualAddressExAarch64 {
    /// Translates a GVA to a GPA.
    fn translate_virtual_address_ex(
        &mut self,
        partition_id: u64,
        vp_index: u32,
        control_flags: defs::TranslateGvaControlFlagsArm64,
        gva_page: u64,
    ) -> HvResult<defs::TranslateVirtualAddressExOutputArm64>;
}

impl<T: TranslateVirtualAddressExAarch64> HypercallDispatch<HvAarch64TranslateVirtualAddressEx>
    for T
{
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, output) = HvAarch64TranslateVirtualAddressEx::parse(params);

        let control_flags = input.control_flags;
        *output = self.translate_virtual_address_ex(
            input.partition_id,
            input.vp_index,
            control_flags,
            input.gva_page,
        )?;

        Ok(())
    }
}

/// Defines the `HvGetVpRegisters` hypercall.
pub type HvGetVpRegisters = RepHypercall<
    defs::GetSetVpRegisters,
    HvRegisterName,
    HvRegisterValue,
    { HypercallCode::HvCallGetVpRegisters.0 },
>;

/// Implements the `HvGetVpRegisters` hypercall.
pub trait GetVpRegisters {
    /// Gets the requested registers.
    fn get_vp_registers(
        &mut self,
        partition_id: u64,
        vp_index: u32,
        vtl: Option<Vtl>,
        registers: &[HvRegisterName],
        output: &mut [HvRegisterValue],
    ) -> HvRepResult;
}

impl<T: GetVpRegisters> HypercallDispatch<HvGetVpRegisters> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (header, input, output, elements_processed) = HvGetVpRegisters::parse(params);
        self.get_vp_registers(
            header.partition_id,
            header.vp_index,
            header.target_vtl.target_vtl()?,
            input,
            output,
        )
        .map_to_hypercall_result(input.len(), elements_processed)
    }
}

/// Defines the `HvSetVpRegisters` hypercall.
pub type HvSetVpRegisters = RepHypercall<
    defs::GetSetVpRegisters,
    HvRegisterAssoc,
    (),
    { HypercallCode::HvCallSetVpRegisters.0 },
>;

/// Implements the `HvSetVpRegisters` hypercall.
pub trait SetVpRegisters {
    /// Sets the requested registers.
    fn set_vp_registers(
        &mut self,
        partition_id: u64,
        vp_index: u32,
        vtl: Option<Vtl>,
        registers: &[HvRegisterAssoc],
    ) -> HvRepResult;
}

impl<T: SetVpRegisters> HypercallDispatch<HvSetVpRegisters> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (header, input, _, elements_processed) = HvSetVpRegisters::parse(params);
        self.set_vp_registers(
            header.partition_id,
            header.vp_index,
            header.target_vtl.target_vtl()?,
            input,
        )
        .map_to_hypercall_result(input.len(), elements_processed)
    }
}

/// Implements the `HvInstallIntercept` hypercall.
pub trait InstallIntercept {
    /// Post a synic message.
    fn install_intercept(
        &mut self,
        partition_id: u64,
        access_type_mask: u32,
        intercept_type: defs::HvInterceptType,
        intercept_parameters: defs::HvInterceptParameters,
    ) -> HvResult<()>;
}

/// Defines the `HvInstallIntercept` hypercall.
pub type HvInstallIntercept =
    SimpleHypercall<defs::InstallIntercept, (), { HypercallCode::HvCallInstallIntercept.0 }>;

impl<T: InstallIntercept> HypercallDispatch<HvInstallIntercept> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, _) = HvInstallIntercept::parse(params);
        self.install_intercept(
            input.partition_id,
            input.access_type_mask,
            input.intercept_type,
            input.intercept_parameters,
        )
    }
}

/// Operations required to handle VTL switch hypercalls.
pub trait VtlSwitchOps {
    /// Advances the instruction pointer for a vtl switch operation whose preconditions have been
    /// satisfied, in the context of the initiating vtl.
    fn advance_ip(&mut self);
    /// Injects an invalid opcode fault for a vtl switch operation whose preconditions have been
    /// violated, in the context of the initiating vtl.
    fn inject_invalid_opcode_fault(&mut self);
}

/// Implements the `HvVtlReturn` hypercall.
pub trait VtlReturn {
    /// Checks if a return to a lower vtl is allowed based on current state.
    fn is_vtl_return_allowed(&self) -> bool;

    /// Return to a lower VTL.
    fn vtl_return(&mut self, fast: bool);
}

/// Defines the `HvVtlReturn` hypercall.
pub type HvVtlReturn = VtlHypercall<{ HypercallCode::HvCallVtlReturn.0 }>;

impl<T: VtlReturn + VtlSwitchOps> HypercallDispatch<HvVtlReturn> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, control) = HvVtlReturn::parse(params);

        // Preconditions for a successful vtl return:
        // 1. Control must be 0 except for the opcode.
        // 2. Input must be 0 or 1.
        // 3. VTL state must allow vtl returns.
        if u64::from(control.with_code(0)) == 0 && (input & !1) == 0 && self.is_vtl_return_allowed()
        {
            // Advance the instruction pointer and issue the vtl return.
            self.advance_ip();
            self.vtl_return(input & 1 != 0);
        } else {
            // Inject an error.
            self.inject_invalid_opcode_fault();
        }

        Ok(())
    }
}

/// Implements the `HvVtlCall` hypercall.
pub trait VtlCall {
    /// Checks if a call to a higher vtl is allowed based on current state.
    fn is_vtl_call_allowed(&self) -> bool;
    /// Calls the higher VTL.
    fn vtl_call(&mut self);
}

/// Defines the `HvVtlCall` hypercall.
pub type HvVtlCall = VtlHypercall<{ HypercallCode::HvCallVtlCall.0 }>;

impl<T: VtlCall + VtlSwitchOps> HypercallDispatch<HvVtlCall> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, control) = HvVtlCall::parse(params);
        // Preconditions for a successful vtl call:
        // 1. Control must be 0 except for the opcode.
        // 2. Input must be 0.
        // 3. VTL state must allow a call to higher VTLs.
        if u64::from(control.with_code(0)) == 0 && input == 0 && self.is_vtl_call_allowed() {
            // Advance the instruction pointer and issue the vtl call.
            self.advance_ip();
            self.vtl_call();
        } else {
            self.inject_invalid_opcode_fault();
        }

        Ok(())
    }
}

/// Implements the `HvEnableVpVtl` hypercall.
pub trait EnableVpVtl<T> {
    /// Enable the specified VTL.
    fn enable_vp_vtl(
        &mut self,
        partition_id: u64,
        vp_index: u32,
        vtl: Vtl,
        vp_context: &T,
    ) -> HvResult<()>;
}

/// Defines the `HvEnableVpVtl` hypercall for x64.
pub type HvX64EnableVpVtl =
    SimpleHypercall<defs::EnableVpVtlX64, (), { HypercallCode::HvCallEnableVpVtl.0 }>;

impl<T: EnableVpVtl<hvdef::hypercall::InitialVpContextX64>> HypercallDispatch<HvX64EnableVpVtl>
    for T
{
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, _) = HvX64EnableVpVtl::parse(params);
        self.enable_vp_vtl(
            input.partition_id,
            input.vp_index,
            Vtl::try_from(input.target_vtl)?,
            &input.vp_vtl_context,
        )
    }
}

/// Defines the `HvEnableVpVtl` hypercall for arm64.
pub type HvArm64EnableVpVtl =
    SimpleHypercall<defs::EnableVpVtlArm64, (), { HypercallCode::HvCallEnableVpVtl.0 }>;

impl<T: EnableVpVtl<hvdef::hypercall::InitialVpContextArm64>> HypercallDispatch<HvArm64EnableVpVtl>
    for T
{
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, _) = HvArm64EnableVpVtl::parse(params);
        self.enable_vp_vtl(
            input.partition_id,
            input.vp_index,
            Vtl::try_from(input.target_vtl)?,
            &input.vp_vtl_context,
        )
    }
}

/// Implements the `HvModifyVtlProtectionMask` hypercall.
pub trait ModifyVtlProtectionMask {
    /// Modify the VTL protection mask for the list of pages specified by `gpa_pages`.
    /// `map_flags` represents the desired permissions for VTLs lower than `target_vtl`.
    /// `target_vtl` must be lower or equal to the current VTL. It cannot be VTL0.
    fn modify_vtl_protection_mask(
        &mut self,
        partition_id: u64,
        map_flags: hvdef::HvMapGpaFlags,
        target_vtl: Option<Vtl>,
        gpa_pages: &[u64],
    ) -> HvRepResult;
}

/// Defines the `HvModifyVtlProtectionMask` hypercall.
pub type HvModifyVtlProtectionMask = RepHypercall<
    defs::ModifyVtlProtectionMask,
    u64,
    (),
    { HypercallCode::HvCallModifyVtlProtectionMask.0 },
>;

impl<T: ModifyVtlProtectionMask> HypercallDispatch<HvModifyVtlProtectionMask> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (header, input, _, elements_processed) = HvModifyVtlProtectionMask::parse(params);
        self.modify_vtl_protection_mask(
            header.partition_id,
            header.map_flags,
            header.target_vtl.target_vtl()?,
            input,
        )
        .map_to_hypercall_result(input.len(), elements_processed)
    }
}

/// Implements the `HvGetVpIndexFromApicId` hypercall.
pub trait GetVpIndexFromApicId {
    /// Gets a list of VP indices from a list of APIC IDs.
    fn get_vp_index_from_apic_id(
        &mut self,
        partition_id: u64,
        target_vtl: Vtl,
        apic_ids: &[u32],
        vp_indices: &mut [u32],
    ) -> HvRepResult;
}

/// Defines the `HvGetVpIndexFromApicId` hypercall.
pub type HvGetVpIndexFromApicId = RepHypercall<
    defs::GetVpIndexFromApicId,
    u32,
    u32,
    { HypercallCode::HvCallGetVpIndexFromApicId.0 },
>;

impl<T: GetVpIndexFromApicId> HypercallDispatch<HvGetVpIndexFromApicId> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (header, input, output, elements_processed) = HvGetVpIndexFromApicId::parse(params);
        self.get_vp_index_from_apic_id(
            header.partition_id,
            Vtl::try_from(header.target_vtl)?,
            input,
            output,
        )
        .map_to_hypercall_result(input.len(), elements_processed)
    }
}

/// Implements the `HvAcceptGpaPages` hypercall.
pub trait AcceptGpaPages {
    /// Accepts the described pages.
    fn accept_gpa_pages(
        &mut self,
        partition_id: u64,
        page_attributes: AcceptPagesAttributes,
        vtl_permission_set: VtlPermissionSet,
        gpa_page_base: u64,
        page_count: usize,
    ) -> HvRepResult;
}

/// Defines the `HvAcceptGpaPages` hypercall.
pub type HvAcceptGpaPages =
    RepHypercall<defs::AcceptGpaPages, u64, (), { HypercallCode::HvCallAcceptGpaPages.0 }>;

impl<T: AcceptGpaPages> HypercallDispatch<HvAcceptGpaPages> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (header, input, _, elements_processed) = HvAcceptGpaPages::parse(params);
        self.accept_gpa_pages(
            header.partition_id,
            header.page_attributes,
            header.vtl_permission_set,
            header.gpa_page_base,
            input.len(),
        )
        .map_to_hypercall_result(input.len(), elements_processed)
    }
}

/// Implements the `HvModifySparseGpaPageHostVisibility` hypercall.
pub trait ModifySparseGpaPageHostVisibility {
    /// Modifies the host page visibility for the listed pages.
    fn modify_gpa_visibility(
        &mut self,
        partition_id: u64,
        visibility: HostVisibilityType,
        gpa_pages: &[u64],
    ) -> HvRepResult;
}

/// Defines the `HvModifySparseGpaPageHostVisibility` hypercall.
pub type HvModifySparseGpaPageHostVisibility = RepHypercall<
    defs::ModifySparsePageVisibility,
    u64,
    (),
    { HypercallCode::HvCallModifySparseGpaPageHostVisibility.0 },
>;

impl<T: ModifySparseGpaPageHostVisibility> HypercallDispatch<HvModifySparseGpaPageHostVisibility>
    for T
{
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (header, input, _, elements_processed) =
            HvModifySparseGpaPageHostVisibility::parse(params);
        self.modify_gpa_visibility(
            header.partition_id,
            header.host_visibility.host_visibility(),
            input,
        )
        .map_to_hypercall_result(input.len(), elements_processed)
    }
}

/// Implements the `HvEnablePartitionVtl` hypercall.
pub trait EnablePartitionVtl {
    /// Enables the VTL for the partition.
    fn enable_partition_vtl(
        &mut self,
        partition_id: u64,
        target_vtl: Vtl,
        flags: defs::EnablePartitionVtlFlags,
    ) -> HvResult<()>;
}

/// Defines the `HvEnablePartitionVtl` hypercall.
pub type HvEnablePartitionVtl =
    SimpleHypercall<defs::EnablePartitionVtl, (), { HypercallCode::HvCallEnablePartitionVtl.0 }>;

impl<T: EnablePartitionVtl> HypercallDispatch<HvEnablePartitionVtl> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (input, _) = HvEnablePartitionVtl::parse(params);
        self.enable_partition_vtl(
            input.partition_id,
            Vtl::try_from(input.target_vtl).map_err(|_| HvError::AccessDenied)?,
            input.flags,
        )
    }
}

/// Implements the `HvFlushVirtualAddressList` hypercall.
pub trait FlushVirtualAddressList {
    /// Invalidates portions of the virtual TLB.
    fn flush_virtual_address_list(
        &mut self,
        processor_set: Vec<u32>,
        flags: defs::HvFlushFlags,
        gva_ranges: &[defs::HvGvaRange],
    ) -> HvRepResult;
}

/// Defines the `HvFlushVirtualAddressList` hypercall.
pub type HvFlushVirtualAddressList = RepHypercall<
    defs::FlushVirtualAddressSpace,
    defs::HvGvaRange,
    (),
    { HypercallCode::HvCallFlushVirtualAddressList.0 },
>;

impl<T: FlushVirtualAddressList> HypercallDispatch<HvFlushVirtualAddressList> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (header, input, _, elements_processed) = HvFlushVirtualAddressList::parse(params);
        let processors =
            parse_processor_masks(1, &[header.processor_mask]).ok_or(HvError::InvalidParameter)?;
        self.flush_virtual_address_list(processors, header.flags, input)
            .map_to_hypercall_result(input.len(), elements_processed)
    }
}

/// Implements the `HvFlushVirtualAddressListEx` hypercall.
pub trait FlushVirtualAddressListEx {
    /// Invalidates portions of the virtual TLB.
    fn flush_virtual_address_list_ex(
        &mut self,
        processor_set: Vec<u32>,
        flags: defs::HvFlushFlags,
        gva_ranges: &[defs::HvGvaRange],
    ) -> HvRepResult;
}

/// Defines the `HvFlushVirtualAddressListEx` hypercall.
pub type HvFlushVirtualAddressListEx = VariableRepHypercall<
    defs::FlushVirtualAddressSpaceEx,
    defs::HvGvaRange,
    (),
    { HypercallCode::HvCallFlushVirtualAddressListEx.0 },
>;

impl<T: FlushVirtualAddressListEx> HypercallDispatch<HvFlushVirtualAddressListEx> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (header, generic_set, input, _, elements_processed) =
            HvFlushVirtualAddressListEx::parse(params);
        let processors = parse_generic_set(generic_set[0], &generic_set[1..])
            .ok_or(HvError::InvalidParameter)?;
        self.flush_virtual_address_list_ex(processors, header.flags, input)
            .map_to_hypercall_result(input.len(), elements_processed)
    }
}

/// Implements the `HvFlushVirtualAddressSpace` hypercall.
pub trait FlushVirtualAddressSpace {
    /// Invalidates all virtual TLB entries.
    fn flush_virtual_address_space(
        &mut self,
        processor_set: Vec<u32>,
        flags: defs::HvFlushFlags,
    ) -> HvResult<()>;
}

/// Defines the `HvFlushVirtualAddressSpace` hypercall.
pub type HvFlushVirtualAddressSpace = SimpleHypercall<
    defs::FlushVirtualAddressSpace,
    (),
    { HypercallCode::HvCallFlushVirtualAddressSpace.0 },
>;

impl<T: FlushVirtualAddressSpace> HypercallDispatch<HvFlushVirtualAddressSpace> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (header, _) = HvFlushVirtualAddressSpace::parse(params);
        let processors =
            parse_processor_masks(1, &[header.processor_mask]).ok_or(HvError::InvalidParameter)?;
        self.flush_virtual_address_space(processors, header.flags)
    }
}

/// Implements the `HvFlushVirtualAddressSpaceEx` hypercall.
pub trait FlushVirtualAddressSpaceEx {
    /// Invalidates all virtual TLB entries.
    fn flush_virtual_address_space_ex(
        &mut self,
        processor_set: Vec<u32>,
        flags: defs::HvFlushFlags,
    ) -> HvResult<()>;
}

/// Defines the `HvFlushVirtualAddressSpaceEx` hypercall.
pub type HvFlushVirtualAddressSpaceEx = VariableHypercall<
    defs::FlushVirtualAddressSpaceEx,
    (),
    { HypercallCode::HvCallFlushVirtualAddressSpaceEx.0 },
>;

impl<T: FlushVirtualAddressSpaceEx> HypercallDispatch<HvFlushVirtualAddressSpaceEx> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (header, generic_set, _) = HvFlushVirtualAddressSpaceEx::parse(params);
        let processors = parse_generic_set(generic_set[0], &generic_set[1..])
            .ok_or(HvError::InvalidParameter)?;
        self.flush_virtual_address_space_ex(processors, header.flags)
    }
}

/// Implements the `HvQuerySparseGpaPageHostVisibility` hypercall.
pub trait QuerySparseGpaPageHostVisibility {
    /// Queries the host page visibility for the listed pages.
    fn query_gpa_visibility(
        &mut self,
        partition_id: u64,
        gpa_pages: &[u64],
        host_visibility: &mut [HostVisibilityType],
    ) -> HvRepResult;
}

/// Defines the `HvQuerySparseGpaPageHostVisibility` hypercall.
pub type HvQuerySparseGpaPageHostVisibility = RepHypercall<
    defs::QuerySparsePageVisibility,
    u64,
    HostVisibilityType,
    { HypercallCode::HvCallQuerySparseGpaPageHostVisibility.0 },
>;

impl<T: QuerySparseGpaPageHostVisibility> HypercallDispatch<HvQuerySparseGpaPageHostVisibility>
    for T
{
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (header, input, output, elements_processed) =
            HvQuerySparseGpaPageHostVisibility::parse(params);
        self.query_gpa_visibility(header.partition_id, input, output)
            .map_to_hypercall_result(input.len(), elements_processed)
    }
}

/// Implements the `HvExtQueryCapabilities` hypercall.
pub trait ExtendedQueryCapabilities {
    /// Queries extended capabilities.
    fn query_extended_capabilities(&mut self) -> HvResult<u64>;
}

/// Defines the `HvExtQueryCapabilities` hypercall.
pub type HvExtQueryCapabilities =
    SimpleHypercall<(), u64, { HypercallCode::HvExtCallQueryCapabilities.0 }>;

impl<T: ExtendedQueryCapabilities> HypercallDispatch<HvExtQueryCapabilities> for T {
    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HvResult<()> {
        let (_, output) = HvExtQueryCapabilities::parse(params);
        *output = self.query_extended_capabilities()?;
        Ok(())
    }
}
