/*******************************************************************************
    Copyright (c) 2013 NVidia Corporation

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to
    deal in the Software without restriction, including without limitation the
    rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    sell copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

        The above copyright notice and this permission notice shall be
        included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*******************************************************************************/

//
//     uvm.h
//
//     This file contains the UVM API declarations, for the userspace-to-kernel
//     calls.
//
//     This API is in transition, from UVM-Lite, to a combined API that is just
//     called "UVM". The combined API will handle all required behavior. For
//     now, the term "UVM-Full" is used as a way to refer to the new, combined
//     API.
//
//     New code should use the items that are declared in the "UVM-Full / UVM
//     8.0" section of this document (the second half). Old (UVM-Lite) code can
//     TEMPORARILY continue to use UvmReserveVA, UvmSetStreamRunning, etc.
//
//     A few UVM-Lite calls had the same name and behavior as the new UVM-Full
//     routines. In those cases, the UVM-Full routines are shown, and the
//     UVM-Lite routines have been removed from this header file. There is no
//     difference in behavior, however. Those calls are:
//
//              UvmInitialize
//              UvmDeinitialize
//              UvmReleaseVA
//              UvmGetFileDescriptor
//              UvmRegisterMpsClient
//              UvmUnregisterMpsClient
//              UvmUnregisterMpsServer
//
//      The following calls had similar names, but different behavior and
//      function signatures, in UVM-Lite and UVM-Full, and were therefore
//      preserved in UVM-Lite (which, remember, will eventually be removed) and
//      renamed with a "UvmFull" prefix for UvmFull:
//
//              UvmReserveVA / UvmFullReserveVA
//              UvmRegisterMpsServer / UvmFullRegisterMpsServer
//

//      UVM API signature modification steps
//      In order to change API signature for any of the APIs defined in this file,
//      a particular sequence of steps has to be followed since the consumer of this
//      API (i.e. CUDA) belongs to a different module branch than the one for this file.
//      Here are the steps to change the signature for a hypothetical API named UvmExampleApi
//      The assumption being made here is that this file is being modified in chips_a.
//      1) Get the revision of the API currently in use by CUDA by looking for the value of
//         UVM_API_REVISION in //sw/dev/gpu_drv/chips_a/drivers/gpgpu/cuda/src/cuda_driver.mk.
//      2) Use the macro UVM_API_REV_IS_AT_MOST to define the two revisions of the API as follows:
//          #if UVM_API_REV_IS_AT_MOST(<revision_used_in_cuda>)
//          // Old UvmExampleApi declaration
//          #else
//          // New UvmExampleApi declaration
//          #endif
//      3) Do the same thing for the function definition, and for any structs that are taken
//         as arguments to these functions.
//      4) Let this change propagate over to cuda_a, so that the CUDA driver can bump up
//         API version and start using the new API.
//      5) Once the cuda_a changes have made it back into chips_a, remove the old API
//         declaration, definition, and any old structs that were in use.

#ifndef _UVM_H_
#define _UVM_H_

#if !defined(UVM_API_REVISION)
#define UVM_API_REVISION 0
#endif

#define UVM_API_REV_IS_AT_MOST(rev) (UVM_API_REVISION <= rev)

#include "uvmtypes.h"

#ifdef __cplusplus
extern "C" {
#endif

/*******************************************************************************
    UvmReserveVA

    Reserves VA space for future use (by UvmRegionCommit). Access to this region
    will cause page faults.

    Alignment: Please note that the reservations may aligned on a larger value
    than the native page size. This means you cannot rely on back-to-back
    non-overlapping reservations to succeed (as both may be rounded up).

    Reservations will always be aligned to boundaries that are at least the
    native page size of the operating system.

    Multiple, non-contiguous VA's (reservations made via this function) are
    allowed.

    Arguments:
        requestedBase: (INPUT)
            Requested base address.

        length: (INPUT)
            Length in bytes of the region.

    Error codes:
      NV_ERR_GENERIC
      NV_ERR_INVALID_ARGUMENT
      NV_ERR_NO_MEMORY
      NV_ERR_INSUFFICIENT_RESOURCES
*/
NV_STATUS UvmReserveVA(void * requestedBase,
                        NvLength length);

/*******************************************************************************
    UvmRegionSetBacking

    This operation sets informs the UVM about the memory backing the allocation.
    The region, as  described by requestedBase and length, must fall within an
    address range that has previously been reserved via UvmReserveVA.

    The gpuUuid argument determines the "home GPU." The "home GPU" is the GPU
    on which hAllocation is done.

    Assumption: You must have already allocated this memory on the GPU. This is
    also known as "mapping this (GPU) memory into a (GPU) channel".

    The requestedBase must be on a page boundary, and length must not be zero
    otherwise this routine will fail.

    Arguments:
        pGpuUuidStruct: (INPUT)
            GPU unique identifier. This is a 16-byte globally unique ID. It is
            available from a variety of API calls and standalone utilities, such
            as the nvidia-smi(.exe) utility.

        hAllocation: (INPUT)
            Physical memory allocation backing the region.

        requestedBase: (INPUT)
            Requested base address.

        length: (INPUT)
            Length in bytes of the region.

    Error codes:
      NV_ERR_GENERIC
      NV_ERR_INVALID_ARGUMENT
      NV_ERR_NO_MEMORY
      NV_ERR_INSUFFICIENT_RESOURCES
*/
NV_STATUS UvmRegionSetBacking(UvmGpuUuid *pGpuUuidStruct,
                              NvU32 hAllocation,
                              void *requestedBase,
                              NvLength length);

/*******************************************************************************
    UvmRegionUnsetBacking

    Mark the region of memory as no longer backed by allocated memory.  The virtual
    address space occupied by these pages will remain reserved regardless of
    whether you reserved the pages beforehand.  If you wish to unset and
    release the reservation of VA, please use UvmReleaseVA instead.

    Arguments:
        requestedBase: (INPUT)
            Requested base address.

        length: (INPUT)
            Length in bytes of the region.

    Error codes:
      NV_ERR_GENERIC
      NV_ERR_INVALID_ARGUMENT
*/
NV_STATUS UvmRegionUnsetBacking(void *requestedBase,
                                NvLength length);

/*******************************************************************************
    UvmRegionCommit

    This operation sets up a region of pages for UVM faulting.  The region, as
    described by requestedBase and length, must fall within an address range
    that has previously been backed via UvmRegionSetBacking.

    The gpuUuid argument determines the "home GPU." The "home GPU" is just a
    term used here to capture the idea that when you call UvmRegionCommit, the
    GPU that you specify at that time is the one that will be used to do
    CPU-to-GPU copies, for example, when UvmSetStreamRunning is called.

    The streamId sets the initial stream.You may use any stream ID, including
    the all-stream or the no-stream. This may be changed later, via the
    UvmRegionSetStream call.

    Assumption: You must have already allocated this memory on the GPU. This is
    also known as "mapping this (GPU) memory into a (GPU) channel".

    The requestedBase must be on a page boundary, and length must not be zero
    otherwise this routine will fail.

    Arguments:
        requestedBase: (INPUT)
            Requested base address.

        length: (INPUT)
            Length in bytes of the region. 

        streamId: (INPUT)
            Identifier of the stream owner. A streamId is unique for the
            lifetime of a process.

        gpuUuid: (INPUT)
            GPU unique identifier. This is a 16-byte globally unique ID. It is
            available from a variety of API calls and standalone utilities, such
            as the nvidia-smi(.exe) utility.

    Error codes:
      NV_ERR_GENERIC
      NV_ERR_INVALID_ARGUMENT
      NV_ERR_NO_MEMORY
      NV_ERR_INSUFFICIENT_RESOURCES
*/
NV_STATUS UvmRegionCommit(void * requestedBase,
                           NvLength length,
                           UvmStream streamId,
                           UvmGpuUuid *pGpuUuidStruct);

/*******************************************************************************
    UvmRegionDecommit

    Mark the region of memory as no longer participating in UVM.  The virtual
    address space occupied by these pages will remain reserved regardless of
    whether you reserved the pages beforehand.  If you wish to decommit and
    release the reservation of VA, please use UvmReleaseVA instead.

    A decommit may span multiple commit operations, but it must not partially
    decommit any single commit.  This will be relaxed later.  It is a limitation
    of RM's memory management.

    Arguments:
        requestedBase: (INPUT)
            Requested base address.

        length: (INPUT)
            Length in bytes of the region.

    Error codes:
      NV_ERR_GENERIC
      NV_ERR_INVALID_ARGUMENT
*/
NV_STATUS UvmRegionDecommit(void * requestedBase,
                             NvLength length);

/*******************************************************************************
    UvmRegionSetStream

    For the GPU that is identified by gpuUuid, set the stream owner to the
    stream specified by newStreamId. Another way of saying that is that it sets
    visibility of the memory region to that of the newStreamId.

    You may also use this to specify the special streams ("no-stream" or
    "all-stream").

    No change is made to the physical backing.

    This ownership is managed at a byte-level granularity, (not page-level).
    That means the different variables on the same memory page may be owned by
    different streams.

    Arguments:
        requestedBase: (INPUT)
            Requested base address.

        length: (INPUT)
            Length in bytes of the region.

        newStreamId: (INPUT)
            The stream ID that will be the new owning stream. A streamId is
            unique for the lifetime of a process.

        gpuUuid: (INPUT)
            GPU unique identifier. This is a 16-byte globally unique ID. It is
            available from a variety of API calls and standalone utilities, such
            as the nvidia-smi(.exe) utility.

    Error codes:
      NV_ERR_GENERIC
      NV_ERR_INVALID_ARGUMENT
*/
NV_STATUS UvmRegionSetStream(void * requestedBase,
                              NvLength length,
                              UvmStream newStreamId,
                              UvmGpuUuid *pGpuUuidStruct);

/*******************************************************************************
    UvmSetStreamRunning

    For all memory visible to the specified stream, CPU-resident dirty pages
    will be migrated back to their home GPU (as previously specified via
    UvmRegionCommit) and marked invalid on the CPU.

    Visibility is defined as memory associated with either all-streams or with
    the specified stream. Memory associated with other streams will not be
    migrated. Sets the stream state to "running".

    After this call returns, CPU accesses to memory associated with streamId
    will fail with undefined behavior.

    Arguments:
        streamId: (INPUT)
            Identifier of the stream that will be set to the "running" state. A
            streamId is unique for the lifetime of a process.

            You may pass in any "normal" stream ID here. The "no-stream" and the
            "all-stream" are not allowed for this argument.

    Error codes:
      NV_ERR_GENERIC
      NV_ERR_INVALID_ARGUMENT
      NV_ERR_NO_MEMORY
      NV_ERR_INSUFFICIENT_RESOURCES
*/
NV_STATUS UvmSetStreamRunning(UvmStream streamId);

/*******************************************************************************
    UvmSetStreamStopped

    Sets the stream state to "stopped". This means that the CPU is allowed to
    access any memory exclusively associated with streamId, and to access memory
    associated with all-streams if no other streams are running. Accesses from
    the CPU will cause GPU-to-CPU memory migrations to occur.

    Arguments:
        streamIdArray: (INPUT)
            An array of identifiers of the streams that will be set to the
            "stopped" state.

            You may pass in any individual stream ID here. The "no-stream" and the
            "all-stream" are not allowed for this argument.

        nStreams: (INPUT)
            Number of streams in the streamIdArray. Must be at least 1.

    Error codes:
      NV_ERR_GENERIC
      NV_ERR_INVALID_ARGUMENT
      NV_ERR_NO_MEMORY
      NV_ERR_INSUFFICIENT_RESOURCES
*/
NV_STATUS UvmSetStreamStopped(UvmStream * streamIdArray, NvLength nStreams);

/*******************************************************************************
    UvmMigrateToGPU
 
    TODO: delete this API call (bug 1638551)

    Forcibly copy data from CPU to GPU memory in preparation for launching the
    associated CUDA kernel. The CPU memory need not be page aligned.

    In addition, pages are also invalidated on the CPU side, unless you pass in
    UVM_MIGRATE_TO_GPU_MIGRATION_FLAGS_DONT_INVALIDATE in the flags argument.

    Arguments:
        requestedBase: (INPUT)
            Requested base address.

        length: (INPUT)
            Length in bytes of the region.

        pUuidStruct: (INPUT)
            A pointer to a GPU UUID struct, that identifies the GPU to which the
            requested region of memory should be migrated.

            GPU unique identifier. This is a 16-byte globally unique ID. It is
            available from a variety of API calls and standalone utilities, such
            as the nvidia-smi(.exe) utility.

        flags: (INPUT)
            Flags to specify modes and hints for migration of data from CPU to
            GPU memory. Only synchronous (i.e. blocking) migration of dirty
            pages is supported for now.

     Error codes:
        NV_ERR_GENERIC
        NV_ERR_INVALID_ARGUMENT
        NV_ERR_NO_MEMORY
*/
NV_STATUS UvmMigrateToGpu(void * requestedBase,
                           NvLength length,
                           UvmGpuUuid * pUuidStruct,
                           unsigned flags);

// default is synchronous (i.e. blocking) migration
#define UVM_MIGRATE_TO_GPU_MIGRATION_FLAGS_ASYNCHRONOUS                      0x1
#define UVM_MIGRATE_TO_GPU_MIGRATION_FLAGS_DONT_INVALIDATE                   0x2

/*******************************************************************************
    UvmRunTest

    Runs a test function denoted by a 32-bit integer. Error messages will be in
    the kernel logs.

    Arguments:
        gpuUuid: (INPUT)
            GPU unique identifier. This is a 16-byte globally unique ID. It is
            available from a variety of API calls and standalone utilities, such
            as the nvidia-smi(.exe) utility.

        testNumber: (INPUT)
            32-bit integer that uniquely identifies a test to be run.

    Error codes:
        NV_ERR_GENERIC
        NV_ERR_INVALID_ARGUMENT
*/
NV_STATUS UvmRunTest(UvmGpuUuid *pGpuUuidStruct, unsigned testNumber);
/*******************************************************************************
    UvmRegisterMpsServer

    Registers the calling process as an MPS (Multi Process Service, a runtime
    architecture designed to allow CUDA processes to share the same GPU hardware
    context) server, and creates migration resources for the GPUs that are in the
    gpuUuid argument.

    The UVM kernel module will create a unique handle and return it in the
    *serverId argument. This handle may be shared with other processes
    (typically, MPS clients), for use when calling UvmRegisterMpsClient.

    This API call will be initially supported on Linux. Non-supported platforms
    will receive an NV_ERR_NOT_SUPPORTED error as the return value, if they
    call this function.

    Arguments:
        gpuUuidList: (INPUT)
            Pointer to a list of GPU UUIDs handled by the MPS server

        numGpus: (INPUT)
            Number of GPU UUIDs in gpuUuidList

        serverId: (OUTPUT)
            Unique identifier to identify the MPS server

    Error codes:
        NV_ERR_NOT_SUPPORTED
        NV_ERR_INVALID_ARGUMENT
        NV_ERR_OBJECT_NOT_FOUND
        NV_ERR_NO_MEMORY
        NV_ERR_GENERIC
*/
NV_STATUS UvmRegisterMpsServer(UvmGpuUuid *gpuUUidArray,
                               NvLength numGpus,
                               NvU64 *serverId);
// --------------------------------------------------------------------------

/**
 * "New" (UVM-Full / UVM 8.0) API
 *
 * Definitions and overview
 *
 * ---------------
 * UVM Coherence:
 * ---------------
 *
 * If either UVM_GPU_ATTRIBUTE_FLAGS_GPU_CACHEABLE or
 * UVM_GPU_ATTRIBUTE_FLAGS_ATOMICS_ENABLED applies to any mapping of a page,
 * then that page is not participating in UVM Coherence.
 *
 * -----------
 * Range Group:
 * -----------
 *
 * A range group is a collection of VA (virtual address) ranges. There is a
 * migratable bit, for the range group. The migratable-ness of each page is
 * determined by:
 *
 * migratable = (range group is migratable ) AND (page is migratable)
 *
 * A "range group" tracks a set of virtual addresses. These may be multiple
 * pairs of base address + length.
 *
 * A given virtual address range in one process can only be in one range group at
 * a time.
 *
 * A new range group is automatically created, when a new virtual address range
 * is added. (UvmAlloc and UvmMigrateRangeGroup do that, and each returns a
 * range group ID.) Range groups are automatically destroyed when the last user
 * releases them, such as on UvmFree or UvmDecommit.
 *
 * Externally mapped allocations (that is, allocations that were created via
 * UvmMapRmAllocation or UvmMapWddmAllocation) cannot have a range group
 * associated with them.
 *
 * -----------------------
 * Home locations of pages
 * -----------------------
 *
 * The concept of home locations for pages exists entirely to support Kepler
 * GPUs. Home locations are therefore mostly ignored in situations that use
 * Pascal-or-better capabilities.
 *
 * Each page may have a home location associated it. The home location may not
 * always be set. The home location of a page can be set via the following API
 * calls:
 *     UvmAlloc
 *     UvmMigrateRangeGroup
 *
 * The home location may be cleared by calling UvmMigrateRangeGroup, and passing
 * in UVM_PROCESSOR_UUID_DEFAULT for the homeProcessorUuid.
 *
 * Please see the documentation of individual API calls, for further information
 * about page home locations and how they influence the UVM driver's behavior in
 * various situations.
 */

union UvmPlatformParamsTag
{
    struct {
        NvHandle hClient;
        NvHandle hVaSpace;
    } rm;
    #if defined(_WIN32)
    struct {
        // assorted Windows handles...we need at least one item for now, in order
        // to prevent compilation failures:
        int dummy;
    } wddm;
    #endif
};

typedef union UvmPlatformParamsTag  UvmPlatformParams;

// Prefetch flags:

#define UVM_PREFETCH_BEHAVIOR_FLAGS_NONE         0x00000000
#define UVM_PREFETCH_BEHAVIOR_FLAGS_FORCE        0x00000001

/*******************************************************************************
 * UvmProcessorUuid is a UUID that can represent any processor, i.e. CPU or GPU.
 * A couple of special UUIDs are defined as follows:
 * 1) CPU UUID : The CPU is assigned a specific UUID. We ignore NUMA systems
 *    here and identify all CPUs with the same UUID. For the default CPU UUID
 *    refer to nvCpuUuid.h in drivers/common/inc
 * 2) "Default GPU" UUID : This is used to clear the home location that is
 *    associated with a given page.
 */
const static UvmProcessorUuid UVM_PROCESSOR_UUID_DEFAULT = {{0}};

/*******************************************************************************
 * UvmInitialize
 *
 * This must be called before any other UVM functions. Repeated calls to
 * UvmInitialize have no visible effect.
 *
 * Error codes:
 *
 *     NV_ERR_NO_MEMORY:
 *         Internal memory allocation failed.
 *
 *     NV_ERR_GENERIC:
 *         Unexpected error. We try hard to avoid returning this error
 *         code,because it is not very informative.
 *
 */
NV_STATUS UvmInitialize(void);

/*******************************************************************************
 * UvmDeinitialize
 *
 * Cleans up all UVM resources associated with the calling process. Any
 * resources that have been shared with other processes and are still being
 * used have to remain valid.
 *
 * Error codes:
 *     NV_ERR_GENERIC:
 *         Unexpected error. We try hard to avoid returning this error
 *         code,because it is not very informative.
*/
NV_STATUS UvmDeinitialize(void);

/*******************************************************************************
 * UvmRegisterGpuVaSpace()
 *
 * TODO: consider getting rid of the platformParams argument, by creating
 * separate API calls for each OS.
 *
 * TODO: should UvmRegisterGpuVaSpace and UvmUnregisterGpuVaSpace have more
 * similar interfaces? For example, either taking a platformParams input or
 * taking handles as input (in which case we might need two different APIs for
 * RM and WDDM).
 *
 * Register the GPU's VA (virtual address) space for use with UVM.
 *
 * Any previously reserved regions with UvmReserveVA() will be reserved on the
 * GPU.
 *
 * Note: may need to add uvmFd and rmFd somewhere (or make a separate RM API
 * call).
 *
 * Arguments:
 *     pGpuUuid: (INPUT)
 *         UUID of the GPU to register.
 *
 *     platformParams: (INPUT)
 *         On Linux: hClient and hVaSpace.
 *         On Windows: (TBD)
 *
 * Error codes:
 *     NV_ERR_NO_MEMORY:
 *         Internal memory allocation failed.
 *
 *     NV_ERR_INVALID_ARGUMENT:
 *         Some other problem with the input arguments was detected.
 *
 *     NV_ERR_GENERIC:
 *         Unexpected error. We try hard to avoid returning this error
 *         code,because it is not very informative.
 *
 *     NV_ERR_GPU_UUID_NOT_FOUND:
 *         The GPU referred to by pGpuUuid was not found.
 *
 *     NV_ERR_OTHER_DEVICE_FOUND:
 *         The UUID does not match the UUID of the device that is associated
 *         with the VA space handles in the platformParams argument.
 *
 *     NV_ERR_INVALID_FLAGS:
 *         The VA space was originally allocated with UVM-incompatible flags.
*/
NV_STATUS UvmRegisterGpuVaSpace(UvmGpuUuid        *pGpuUuid,
                                UvmPlatformParams *platformParams);

/*******************************************************************************
 *  UvmUnregisterGpuVaSpace()
 *
 *  Unregister the GPU VA space that was previous registered via a call to
 *  UvmRegisterGpuVaSpace().
 *
 *  1) Unmap all mappings created by UVM on that gpu.
 *  2) Evict any pages resident on the GPU
 *
 *  Arguments:
 *      pGpuUuid: (INPUT)
 *         UUID of the GPU to unregister.
 *
 * Error codes:
 *     NV_ERR_NO_MEMORY:
 *         Internal memory allocation failed.
 *
 *     NV_ERR_INVALID_ARGUMENT:
 *         Some other problem with the input arguments was detected.
 *
 *     NV_ERR_GENERIC:
 *         Unexpected error. We try hard to avoid returning this error
 *         code,because it is not very informative.
 *
 *     NV_ERR_GPU_UUID_NOT_FOUND:
 *         The GPU referred to by pGpuUuid was not found.
*/
NV_STATUS UvmUnregisterGpuVaSpace(UvmGpuUuid *pGpuUuid);

#if UVM_API_REV_IS_AT_MOST(1)

/*******************************************************************************
 *  UvmRegisterChannel()
 *
 *  TODO: fully explain what this does...this API call is still being designed.
 *  TODO: is this API call required or desired, for WDDM? Figure that out.
 *
 *  Register a channel for use with UVM.
 *
 *  Arguments:
 *      pGpuUuid: (INPUT)
 *         UUID of the GPU to unregister.
 *
 *      hClient: (INPUT)
 *         Handle of a previously created RM (resman) client context.
 *
 *      hChannel: (INPUT)
 *         RM handle of a previously created GPU channel.
 *
 *      hUvmChannelHandle: (OUTPUT)
 *         This is returned to the caller, and must be used for any future calls
           to UvmUnregisterChannel.
 *
 * Error codes:
 *     NV_ERR_INVALID_ARGUMENT:
 *         A NULL pointer was passed, for pGpuUuid.
 *
 *         Or, some other problem with the input arguments was detected.
 *
 *     NV_ERR_GPU_UUID_NOT_FOUND:
 *         The GPU referred to by pGpuUuid was not found.
 *
 *     NV_ERR_NO_MEMORY:
 *         Internal memory allocation failed.
 *
 *     NV_ERR_GENERIC:
 *         Unexpected error. We try hard to avoid returning this error
 *         code,because it is not very informative.
*/
NV_STATUS UvmRegisterChannel(UvmGpuUuid *pGpuUuid,
                             NvHandle    hClient,
                             NvHandle    hChannel,
                             NvHandle   *hUvmChannelHandle);

#else

/*******************************************************************************
 *  UvmRegisterChannel()
 *
 *  TODO: fully explain what this does...this API call is still being designed.
 *  TODO: is this API call required or desired, for WDDM? Figure that out.
 *
 *  Register a channel for use with UVM.
 *
 *  Arguments:
 *      pGpuUuid: (INPUT)
 *         UUID of the GPU to unregister.
 *
 *      hClientFromRm: (INPUT)
 *         Handle of a previously created RM (resman) client context.
 *
 *      hChannelFromRm: (INPUT)
 *         RM handle of a previously created GPU channel.
 *
 *      base: (INPUT)
 *          Base address (starting point) of the VA (virtual address) range
 *          reserved for mapping the allocations associated with this channel. 
 *
 *      length: (INPUT)
 *          Length, in bytes, of the range.
 *
 * Error codes:
 *     NV_ERR_INVALID_ARGUMENT:
 *         A NULL pointer was passed, for pGpuUuid.
 *
 *         Or, some other problem with the input arguments was detected.
 *
 *     NV_ERR_GPU_UUID_NOT_FOUND:
 *         The GPU referred to by pGpuUuid was not found.
 *
 *     NV_ERR_NO_MEMORY:
 *         Internal memory allocation failed.
 *
 *     NV_ERR_GENERIC:
 *         Unexpected error. We try hard to avoid returning this error
 *         code,because it is not very informative.
*/
NV_STATUS UvmRegisterChannel(UvmGpuUuid *pGpuUuid,
                             NvHandle    hClientFromRm,
                             NvHandle    hChannelFromRm,
                             void*       base,
                             NvLength    length);

#endif

/*******************************************************************************
 *  UvmUnregisterChannel()
 *
 *  TODO: fully explain what this does...this API call is still being designed.
 *
 *  Unregister a channel from UVM. The channel must have been previously
 *  registered via a call to UvmRegisterChannel.
 *
 *  Arguments:
 *      pGpuUuid: (INPUT)
 *         UUID of the GPU to unregister.
 *
 *      hChannelFromRm: (INPUT)
 *         RM handle of a previously created (and registered) GPU channel.
 *
 * Error codes:
 *     NV_ERR_INVALID_ARGUMENT:
 *         A NULL pointer was passed, for pGpuUuid.
 *
 *     NV_ERR_GPU_UUID_NOT_FOUND:
 *         The GPU referred to by pGpuUuid was not found.
 *         Or, some other problem with the input arguments was detected.
 *
 *     NV_ERR_GENERIC:
 *         Unexpected error. We try hard to avoid returning this error
 *         code,because it is not very informative.
*/
NV_STATUS UvmUnregisterChannel(UvmGpuUuid *pGpuUuid,
                               NvHandle    hChannelFromRm);

/*******************************************************************************
    UvmFullReserveVA

    Reserves VA space for future use.

    The VA will be reserved on the CPU and all registered GPUs.

    Reservations will always be aligned to boundaries that are at least the
    native page size of the operating system.

    Multiple, non-contiguous VA's (reservations made via this function) are
    allowed.

    Arguments:
        base: (INPUT/OUTPUT)
            Requested base address. Cases:

            a) base acts as an OUTPUT parameter: If you set *base = NULL, then
            this call will attempt to choose a base address for you. If that
            cannot be done (some platforms in some situations will lead to not
            being able to do this), then this call will return an error.

            b) base acts as an INPUT parameter: If you set *base to some non-null
            value, then this call will attempt to use that address as the base
            address. If that is not possible, then this call will return an
            error.

        length: (INPUT)
            Length in bytes of the region.

        maxVaBits: (INPUT)
            Upper limit on the number of addressable bits in the VA space. For
            example, 40 bits would be specified here, in order to stay within
            the lower 40 bits of the virtual address space.

        log2Alignment: (INPUT)
            pages must be aligned to 2**(log2Alignment).

            If you specify less than 12 (which corresponds to 4KB pages), this
            call will return an error.

            If you specify a value that corresponds to a page size that is less
            than the native page size on the platform, this call will return an
            error. (For example, some IBM PPC systems have a native page size of
            64 KB).

 * Error codes:
 *     NV_ERR_NO_MEMORY:
 *         Internal memory allocation failed.
 *
 *     NV_ERR_UVM_ADDRESS_IN_USE: *base was an input (non-NULL) address, but
 *     this call was unable to use that address.
 *
 *     NV_ERR_NOT_SUPPORTED:
 *         Caller specified base as an output parameter (*base = NULL), but the
 *         platform was unable to support this.
 *
 *     NV_ERR_INVALID_ARGUMENT: One of the following occurred:
 *         Caller requested an alignment that is too small.
 *         Caller specified a length of zero.
 *         Caller requested a base address that is above the maxVaBits range.
 *
 *     NV_ERR_GENERIC:
 *         Unexpected error. We try hard to avoid returning this error
 *         code,because it is not very informative.
 *
*/
NV_STATUS UvmFullReserveVA(void     **base,
                           NvLength   length,
                           NvU32      maxVaBits,
                           NvU32      log2Alignment);

/*******************************************************************************
    UvmReleaseVA

    Releases all pages within the VA range. If any of the pages were committed,
    they are automatically decomitted as well.

    The release may encompass more than a single reserve VA or commit call, but
    must not partially release any regions that were either reserved or
    committed previously.

    Arguments:
        base: (INPUT)
            Base address (starting point) of the VA (virtual address) range.

        length: (INPUT)
            Length, in bytes, of the range.

    Error codes:

        NV_ERR_NO_MEMORY:
            Internal memory allocation failed. There is likely more than one
            possible cause of this error.

        NV_ERR_INVALID_ARGUMENT:
            Some other problem with the input arguments was detected.

        NV_ERR_GENERIC:
            Unexpected error. We try hard to avoid returning this error
            code,because it is not very informative.
*/
NV_STATUS UvmReleaseVA(void *base, NvLength length);

/*******************************************************************************
 * UvmCreateRangeGroup
 *
 * Creates a new range group ID and returns it to the caller.
 *
 * MPS Mode: Any range group that an MPS server or any of its clients creates,
 * will be assigned a unique rangeGroupId (unique for that MPS group).
 *
 * ARGUMENTS
 *
 *      rangeGroupId: (OUTPUT)
 *          ID of the newly created range group.
 *
 * ERRORS:
 *
 *      NV_ERR_NO_MEMORY
 *
 *      NV_ERR_INVALID_ARGUMENT: a NULL pointer was passed in the rangeGroupId
 * argument.
 */
NV_STATUS UvmCreateRangeGroup(UvmRangeGroupId *rangeGroupId);

/*******************************************************************************
 *  UvmDestroyRangeGroup
 *
 *  Any pages that were in the range group are now no longer associated with a
 *  range group.
 *
 *  If there are any pages associated with this range group, that association is
 *  cleared. i.e. the behavior is the same as setting the range group id for
 *  those pages to UVM_RANGE_GROUP_ID_NONE.
 *
 *  Error: Invalid value if rangeGroupId was not allocated using
 *  UvmCreateRangeGroup.
 *
 */
NV_STATUS UvmDestroyRangeGroup(UvmRangeGroupId rangeGroupId);

/*******************************************************************************
 * UvmSetRangeGroup
 *
 * Sets the range group for the specified VA range, and migrates pages if
 * necessary. If any pages in that VA range were associated with another range
 * group, that association is changed to this range group.
 *
 * If the rangeGroupId is a range group that is non-migratable (has its
 * migratable bit cleared), then all pages in the base+length range will be
 * migrated to their home location, if they are not already there. This is
 * because we need to avoid any case in which a page is not migratable, but is
 * not at its home location.
 *
 * As for pages' home locations, please see notes 1-3 in the
 * UvmPreventMigrationRangeGroups.
 *
 *  If rangeGroupId is UVM_RANGE_GROUP_ID_NONE, then all pages in the VA range
 *  will have their range group association removed.
 *
 * Arguments:
 *      base: (INPUT)
 *          Base address (starting point) of the VA (virtual address) range.
 *
 *      length: (INPUT)
 *          Length, in bytes, of the range.
 *
 *  Errors:
 *       NV_ERR_OBJECT_NOT_FOUND:
 *           rangeGroupId was not found. If the rangeGroupId was not previously
 *           created, via a call to UvmCreateRangeGroup, then this call will
 *           return this error.
 *
 *  The base+length range must have already been created, via either UvmAlloc or
 *  UmvMemMap. Otherwise, this call will return an error.
 *
 */
NV_STATUS UvmSetRangeGroup(UvmRangeGroupId  rangeGroupId,
                           void             *base,
                           NvLength         length);

/*******************************************************************************
 * UvmAlloc
 *
 * TODO : Specify the value of uninitialized data (should it be zero or not).
 *
 * Allocates memory on the destination processor.
 *
 * Please see the documentation about range groups (above) for details about
 * their meaning and use.
 *
 * If any GPU in the perGpuAttributes is a Kepler GPU, and the
 * UVM_GPU_ATTRIBUTE_FLAGS_MAPPED is set for that GPU, then all memory in the
 * base+length range must be populated (allocated) on the destination processor.
 * Otherwise, this call will fail and return NV_ERR_NO_MEMORY.
 *
 * A page of memory can only ever be a member of one range group at a time. That
 * means that migrating to a new range group causes the pages to be removed from
 * the source range group.
 *
 * Cases for this API call:
 *
 * Case 1: Adding a base+length range to an existing range group. Note that you
 * can also use UvmSetRangeGroup to add a base+length range to an existing range
 * group, but UvmAlloc allows also specifying additional processor-specific
 * items, including homeProcessorUuid, perGpuFlags, allocFlags, and a
 * semaphoreToRelease for tracking asynchronous operations.
 *
 * Arguments:
 *     rangeGroupId: (INPUT)
 *         Unique identifier of a range group object.
 *
 *     base: (INPUT)
 *         Base address (starting point) of the VA (virtual address) range.
 *
 *     length: (INPUT)
 *         Length, in bytes, of the range.
 *
 *     homeProcessorUuid: (INPUT)
 *         UUID of the processor. This is the home location for all of the pages in
 *         the base+length range.
 *
 *     perGpuAttributes: (INPUT)
 *     gpuAttributesCount: (INPUT)
 *         Attributes for any of the GPUs that require them. Some GPUs may be
 *         ommitted from this list, and in fact, the list can be empty
 *         (gpuAttributesCount = 0).
 *
 *         If a GPU is not included in this list, then a mapping of the
 *         base+length range will NOT be created for that GPU.
 *
 * Errors:
 *
 * If the base+length memory range overlaps with any base+length range that has
 * been previously allocated (in this process) via UvmAlloc or UvmMemMap, then
 * this call will return an error.
 *
 * If you have not already called UvmReserveVA, specifying a range that covers
 * the base+length range passed into this UvmAlloc call, then this call will
 * return an error: NV_ERR_INVALID_ARGUMENT.
 *
 *     NV_ERR_OBJECT_NOT_FOUND:
 *         rangeGroupId was not found.
 *
 *      NV_ERR_NO_MEMORY:
 *          Internal memory allocation failed.
 *
 *      NV_ERR_INVALID_ARGUMENT:
 *          Some other problem with the input arguments was detected.
 *
 *      NV_ERR_INVALID_STATE:
 *          An MPS client attempted to call UvmAlloc. (Please see the various
 *          MPS API calls, for details about MPS operation.)
 *
 *      NV_ERR_GENERIC:
 *          Unexpected error. We try hard to avoid returning this error code,
 *          because it is not very informative.
 *
 */
NV_STATUS UvmAlloc(UvmRangeGroupId      rangeGroupId,
                   void                *base,
                   NvLength             length,
                   UvmProcessorUuid    *homeProcessorUuid,
                   UvmGpuAttributes    *perGpuAttributes,
                   NvLength             gpuAttributesCount);

/*******************************************************************************
 * UvmUpdateGpuAttributes
 *
 * base+length must be contained within a previously allocated range. In other
 * words, UvmAlloc or UvmMemMap must have been called, specifying a range that
 * covers the base+length range that is being passed in to
 * UvmUpdateGpuAttributes. If not, then this call will return an error:
 * NV_ERR_INVALID_ARGUMENT.
 *
 * TODO: what happens if we specify UNMAPPED for all GPUs in the attributes?
 *
 * If base+length refers to memory that is being used as semaphores, then this
 * API call will:
 *
 * 1) Wait for the last semaphore to be released.
 *
 * 2) It will also prevent new acquires on the semaphores, for the duration of
 * this call.
 *
 * 3) It will also then the UVM kernel driver's mirrored mapping, to have the
 * same attributes that are being requested by this call.
 *
 * 4) At this point, new acquires on the semaphores are once again allowed.
 *
 * Arguments:
 *      base: (INPUT)
 *          Base address (starting point) of the VA (virtual address) range.
 *
 *      length: (INPUT)
 *          Length, in bytes, of the range.
 *
 *      perGpuAttributes: (INPUT)
 *      gpuAttributesCount: (INPUT)
 *          Attributes for any of the GPUs that require them. Some GPUs may be
 *          ommitted from this list, and in fact, the list can be empty
 *          (gpuAttributesCount = 0).
 */
NV_STATUS UvmUpdateGpuAttributes(void                *base,
                                 NvLength             length,
                                 UvmGpuAttributes    *perGpuAttributes,
                                 NvLength             gpuAttributesCount);

/*******************************************************************************
 * UvmFree
 *
 * Frees memory on the destinationProcessor. Any mappings associated with the
 * allocation are also freed.
 *
 * During API design reviews, there was some debate about whether to require the
 * caller to pass in both the base (starting address of the range to be freed)
 * and the original length that was passed to whichever call was made to
 * allocate the range. However, we eventually decided to only pass in the base
 * argument, omitting the length, because:
 *
 * a) Doing so makes UvmFree easier to use, because there is no need to track
 *    [pointer, length] pairs,
 *
 * b) The chosen version of UvmFree looks exactly like free(3), matching the
 *    malloc/free (C library) idiom, a widely understood and popular interface,
 *    and
 *
 * c) If the caller were to pass a length to UvmFree, and the UvmFree
 *    implementation (user space, kernel) attempted to provide robust behavior,
 *    then the implementation would need to verify that the length is valid: not
 *    too long, not too short. This, in turn, requires the implementation to
 *    track the [pointer, length] pairs--so making the caller do that also is
 *    wasteful duplication of code.
 *
 * MPS notes:
 * 1) Both the server and the client have to call UvmFree.
 *
 * 2) When a client calls UvmFree, it only makes the VA range available to the
 * client. It does not free the allocation on the server. The server needs to
 * call UvmFree as well to truly free the allocation.
 *
 * 3) Calling UvmFree on the MPS server will unmap it on all MPS clients that
 * have it mapped. The client still needs to call UvmFree in order to reclaim
 * the VA range in the client's address space.
 *
 * Arguments:
 *     base: (INPUT)
 *         Starting address of the range to be freed. This must be match an
 *         address that was obtained via UvmAlloc or UvmMemMap or
 *         UvmMapRmAllocation or UvmMapWddmAllocation.
*/
void UvmFree(void *base);

// This is just UvmFree with return status (for testing purposes)
NV_STATUS UvmFreeInternal(void *base);

/*******************************************************************************
 * UvmPrefetch()
 *
 * Attempts to copy the contents of memory, for the virtual address range, to
 * the specified (destinationUuid) processor.
 *
 * Unless you specify UVM_PREFETCH_BEHAVIOR_FLAGS_FORCE, this is a performance
 * hint, and as such, the actual behavior may vary. You must use one of the
 * migration function calls (such as UvmPreventMigration or
 * UvmPreventMigrationKepler) if you actually need to guarantee that migration
 * really occurs. Or, you can specify the UVM_PREFETCH_BEHAVIOR_FLAGS_FORCE to
 * force the pages to really be prefetched.
 *
 * If UvmPreventMigration has previously been called on any page within the
 * base+length range, and the current location of those pages does not match
 * destinationUuid, then those pages will not be prefetched. In that case, or in
 * any case in which fewer than the full set of pages could be prefetched, this
 * call will return NV_WARN_MORE_PROCESSING_REQUIRED.
 *
 * If UvmPrefetch does move data, or the range has not been populated (due to,
 * for example, either ZFOD or COW), these are the steps that it will follow for
 * those pages that get migrated. These steps are either identical, or nearly
 * so, to those followed by the UvmMigrateRangeGroup API call:
 *
 * 1) Allocate the backing for the VA on the destination processor.
 *
 * 2) Unmap any writable pages on all the other processors.
 *
 * 3) Any read-only pages can remain mapped to local copies.
 *
 * 4) Copy any pages currently backed on other processors to the new backing.
 *
 * 5) Any read-only pages may be duplicated.
 *
 * 6) Map the new backing on the processor.
 *
 * 7) Free the old backing of any writable pages.
 *
 * Arguments:
 *     base: (INPUT)
 *         Base address (starting point) of the VA (virtual address) range.
 *
 *     length: (INPUT)
 *         Length, in bytes, of the range.
 *
 *     destinationUuid: (INPUT)
 *         UUID of the processor that the prefetch is targetting (destination processor).
 *
 * Error codes:
 *     NV_ERR_GPU_UUID_NOT_FOUND:
 *         destinationUuid was not found.
 *
 *     NV_ERR_NO_MEMORY:
 *         Internal memory allocation failed.
 *
 *     NV_WARN_MORE_PROCESSING_REQUIRED:
 *         Less than 100 percent of the requested range was actually prefetched.
 *         However, there is no need to call this function again, because all
 *         the pages that could reasonably be prefetched, were prefetched. In
 *         other words, difficult pages were skipped, but prefetching continued.
 *
 *     NV_ERR_INVALID_ARGUMENT:
 *         Some other problem with the input arguments was detected.
 *
 *     NV_ERR_GENERIC:
 *         Unexpected error. We try hard to avoid returning this error code,
 *         because it is not very informative.
 */
NV_STATUS UvmPrefetch(void             *base,
                      NvLength          length,
                      UvmProcessorUuid *destinationUuid,
                      NvU32             prefetchFlags);

/*******************************************************************************
 * UvmMigrate()
 *
 * Migrates the backing of the virtual address range to the specified processor.
 * This is a temporary API. This functionality will probably be supported by a
 * synchronous option for UvmPrefetch, which is still being designed.
 *
 * If UvmPreventMigration has previously been called on any page within the
 * base+length range, and the current location of those pages does not match
 * destinationUuid, then those pages will not be migrated. In that case, or in
 * any case in which fewer than the full set of pages could be migrated, this
 * call will return NV_WARN_MORE_PROCESSING_REQUIRED.
 *
 * If UvmMigrate does move data, or the range has not been populated (due to,
 * for example, either ZFOD or COW), these are the steps that it will follow for
 * those pages that get migrated. These steps are either identical, or nearly
 * so, to those followed by the UvmMigrateRangeGroup API call:
 *
 * 1) Allocate the backing for the VA on the destination processor.
 *
 * 2) Unmap any writable pages on all the other processors.
 *
 * 3) Any read-only pages can remain mapped to local copies.
 *
 * 4) Copy any pages currently backed on other processors to the new backing.
 *
 * 5) Any read-only pages may be duplicated.
 *
 * 6) Map the new backing on the processor.
 *
 * 7) Free the old backing of any writable pages.
 *
 * Arguments:
 *     base: (INPUT)
 *         Base address (starting point) of the VA (virtual address) range.
 *
 *     length: (INPUT)
 *         Length, in bytes, of the range.
 *
 *     destinationUuid: (INPUT)
 *         UUID of the destination processor.
 *
 * Error codes:
 *     NV_ERR_GPU_UUID_NOT_FOUND:
 *         destinationUuid was not found.
 *
 *     NV_ERR_NO_MEMORY:
 *         Internal memory allocation failed.
 *
 *     NV_ERR_INVALID_ARGUMENT:
 *         Some other problem with the input arguments was detected.
 *
 *     NV_ERR_GENERIC:
 *         Unexpected error. We try hard to avoid returning this error code,
 *         because it is not very informative.
 */
NV_STATUS UvmMigrate(void             *base,
                     NvLength          length,
                     UvmProcessorUuid *destinationUuid);

/*******************************************************************************
 * UvmPrefetchRangeGroup()
 *
 * Provides the same behavior and constraints as UvmPrefetch, but operates on a
 * range group, rather than a virtual address range. Please see the documentation
 * earlier in this file, for details about range groups.
 *
 * Arguments:
 *     rangeGroupId: (INPUT)
 *         Unique identifier of a range group. This range group must have been
 *         previously created via a call to either UvmCreateRangeGroup or
 *         UvmAlloc.
 *
 *     destinationUuid: (INPUT)
 *         Same as for UvmPrefetch.
 *
 * Error codes:
 *    NV_ERR_OBJECT_NOT_FOUND:
 *        rangeGroupId was not found.
 *
 *    Remaining error codes are the same as for UvmPrefetch.
 */
NV_STATUS UvmPrefetchRangeGroup(UvmRangeGroupId   rangeGroupId,
                                UvmProcessorUuid *destinationUuid);

/*******************************************************************************
 * UvmMigrateRangeGroup
 *
 * TODO : Specify the value of uninitialized data (should it be zero or not).
 *
 * Migrates all the ranges in the range group (UvmMigrateRangeGroup) to the
 * specified processor, and updates internal (kernel driver) accounting so that
 * this processor is the new "home location" for each page that is part of the
 * specified base+length range of memory.
 *
 * If the caller specifies UVM_PROCESSOR_UUID_DEFAULT for homeProcessorUuid,
 * then no migration occurs, but the home location of each page in the range
 * group is cleared (which means, setting it to UVM_PROCESSOR_UUID_DEFAULT).
 *
 * UvmMigrateRangeGroup can cause pages to be migrated even if those pages are
 * marked as "not migratable", via prior calls to UvmPreventMigration or
 * UvmPreventMigrationRangeGroups.
 *
 * If any GPU in the perGpuAttributes is a Kepler GPU, and the
 * UVM_GPU_ATTRIBUTE_FLAGS_MAPPED is set for that GPU, then all memory in the
 * base+length range must be populated (allocated) on the destination processor.
 * Otherwise, this call will fail and return NV_ERR_NO_MEMORY. Also, in this
 * case, it is illegal to pass in UVM_PROCESSOR_UUID_DEFAULT for homeProcessorUuid.
 *
 * If any page in the base+length range is not migratable (has its migratable
 * bit cleared), and the page's current location is different from the
 * homeProcessorUuid, then this call will fail and return an error.
 *
 * Note: this is intended for CUDA to migrate pages back to system memory,
 * in the case that a pre-Pascal (Kepler) GPU enters the system. (Multi-GPU
 * scenario.)
 *
 * These are the approximate steps that will occur, during this API call:
 *
 * 1) Allocate the backing for the base+length range on the processor.
 *
 * 2) Unmap any writable pages from all the other processors.
 *
 * 3) Any read-only pages may remain mapped to local copies.
 *
 * 4) Copy any pages currently backed on other processors to the new backing.
 *
 * 5) Free the old backing of any writable pages
 *
 * 6) Any read-only pages may be duplicated
 *
 * 7) GPU mappings will be created, in accordance with the perGpuAttributes
 * argument.
 *
 * Arguments:
 *     rangeGroupId: (INPUT)
 *         Unique identifier of a range group object.
 *
 *    homeProcessorUuid: (INPUT)
 *         UUID of the home processor. This is the new home location for all of
 *         the pages in the base+length range.
 *
 *     perGpuAttributes: (INPUT)
 *     gpuAttributesCount: (INPUT)
 *         Attributes for any of the GPUs that require them. Some GPUs may be
 *         ommitted from this list, and in fact, the list can be empty
 *         (gpuAttributesCount = 0).
 *
 *         If a GPU is not included in this list, then a mapping of the
 *         base+length range will NOT be created for that GPU.
 *
 * Error codes:
 *     NV_ERR_OBJECT_NOT_FOUND:
 *         rangeGroupId was not found.
 *
 *     NV_ERR_NO_MEMORY:
 *         Memory allocation failed. See above for some specific examples.
 *
 *     NV_ERR_INVALID_ARGUMENT:
 *         The caller specified UVM_PROCESSOR_UUID_DEFAULT for
 *         homeProcessorUuid, and also specified UVM_GPU_ATTRIBUTE_FLAGS_MAPPED
 *         for the attributes of a Kepler GPU, in perGpuAttributes.
 *
 *     NV_ERR_INVALID_OPERATION:
 *         One or more pages in the base+length range is not migratable (has its
 *         migratable bit cleared), and the page's current location is different
 *         from the homeProcessorUuid.
 *
 *     NV_ERR_GENERIC:
 *         Unexpected error. We try hard to avoid returning this error
 *         code,because it is not very informative.
 */
NV_STATUS UvmMigrateRangeGroup(UvmRangeGroupId     rangeGroupId,
                               UvmProcessorUuid   *homeProcessorUuid,
                               UvmGpuAttributes   *perGpuAttributes,
                               NvLength            gpuAttributesCount,
                               NvU32               migrationFlags);

/*******************************************************************************
 * UvmMemMap
 *
 * Mark a region as valid for faulting: faults will be handled by the UVM
 * driver.
 *
 * The memory range that is specified by (base, length) must have been
 * previously reserved, via a call to UvmReserveVA.
 *
 * You cannot call UvmMemMap on a region that has already had UvmMemMap called
 * on it. This call will fail and return an error if that is attempted.
 *
 * Upon returning from this call, all pages will appear to be initialized to
 * zero. That is to say, when any of the pages are read, they will show zeros.
 *
 * The mapping is initially writable (read/write).
 *
 * This operation call be un-done via UvmFree.
 *
 * MPS notes:
 * 1) This call will fail in the MPS client if the MPS server has not
 * already made a corresponding call to either UvmAlloc or UvmMemMap for any
 * page in this memory range.
 *
 * 2) It is legal for the GPU to fault on this memory range before the client
 * has called UvmMemMap on it.
 *
 * Arguments:
 *     base: (INPUT)
 *         Base address (starting point) of the VA (virtual address) range.
 *
 *     length: (INPUT)
 *         Length, in bytes, of the range.
 *
 * Errors:
 *     NV_ERR_UVM_ADDRESS_IN_USE:
 *         The requested address range is not available.
 *
 *     NV_ERR_INVALID_ARGUMENT:
 *         The MPS client passed in a (base, length) that didn't match the
 *         server.
*/
NV_STATUS UvmMemMap(void *base, NvLength length);

/*******************************************************************************
 * UvmMapRmAllocation
 *
 * TODO: figure out how CPU mappings are created and managed. Note that
 * UvmMapRmAllocationCpu (a possible new routine), or RmMapMemory, could both be
 * used to create CPU mappings. If UvmMapRmAllocationCpu is used, then the UVM
 * driver will need the ability to create BAR1 mappings. If RmMapMemory is used,
 * then it must be able to effectively unmap (or remap) regions that were
 * previous mapped via UvmReserveVA.
 *
 * This call will also work on WDDM platforms, despite the fact that there is a
 * separate UvmMapWddmAllocation API call.
 *
 * (Other related calls, such as Android/Mobile/Tegra, may be added as the need
 * arises.)
 *
 * The caller allocates memory from RM (or some other module, such as the Tegra
 * kernel driver, or Nouveau) normally, then the caller maps memory into the UVM
 * address space.
 *
 * This call neither creates nor modifies any CPU mappings, even if the
 * base+length range came from a previously reserved region (reserved via
 * UvmReserveVA). This implies that CPU accesses to this range will still cause
 * a fault, if not mapped.
 *
 * The underlying memory will not be freed, nor will the GPU mappings be
 * removed, until UvmFree is called. No exceptions.
 *
 * If the base+length range overlaps any existing mapping, then this call will
 * fail and return NV_ERR_UVM_ADDRESS_IN_USE.
 *
 * The caller (CUDA, for example) is expected to use this call to map special
 * allocations, such as context switch area, semaphores, and pushbuffer memory.
 *
 * These allocations must already be non-migratable before calling this
 * function. Furthermore, once this call completes, you may not use this
 * base+length range in any UVM API calls, except for UvmFree or
 * UvmUpdateGpuAttributes.
 *
 * Arguments:
 *      base: (INPUT)
 *          Base address (starting point) to which the external allocation will
 *          be mapped.
 *
 *      length: (INPUT)
 *          Length, in bytes, of the range.
 *
 *      perGpuAttributes: (INPUT)
 *      gpuAttributesCount: (INPUT)
 *          Attributes for any of the GPUs that require them. Some GPUs may be
 *          ommitted from this list, and in fact, the list can be empty
 *          (gpuAttributesCount = 0).
 *
 *      hClientFromRm: (INPUT)
 *          Client handle that was used when allocating the memory from NVIDIA
 *          Resource Manager (RM).
 *
 *      hMemoryFromRm: (INPUT)
 *          Memory handle that was used when allocating the memory from NVIDIA
 *          Resource Manager (RM).
 *
 * Errors:
 *      NV_ERR_INSUFFICIENT_RESOURCES:
 *           Could not map the memory.
 *
 *      NV_ERR_INVALID_ARGUMENT:
 *          Some other problem with the input arguments was detected.
 *
 *      NV_ERR_GENERIC:
 *          Unexpected error. We try hard to avoid returning this error code,
 *          because it is not very informative.
 */
NV_STATUS UvmMapRmAllocation(void               *base,
                             NvLength            length,
                             UvmGpuAttributes   *perGpuAttributes,
                             NvLength            gpuAttributesCount,
                             NvU32               hClientFromRm,
                             NvU32               hMemoryFromRm);

/*******************************************************************************
 * UvmMapWddmAllocation
 *
 * TODO: figure out if this function needs the homeProcessorUuid argument.
 *
 * This creates a mirrored allocation of an allocation that Microsoft made in a
 * 40-bit (Microsoft-controlled) address space, into a 49-bit address space.
 * Note that when CUDA launches a compute kernel (shader), it runs in the
 * 49-bit address space.
 *
 * (Other related calls, such as Android/Mobile/Tegra, may be added as the need
 * arises.)
 *
 * Please see UvmMapRmAllocation (above) for details. This call is similar, but
 * is specifically intended for WDDM platforms.
 *
 * Arguments:
 *     base: (INPUT)
 *         Base address (starting point) to which the external allocation will
 *         be mapped.
 *
 *     length: (INPUT)
 *         Length, in bytes, of the range.
 *
 *     homeProcessorUuid: (INPUT)
 *         UUID of the processor.
 *
 *     perGpuAttributes: (INPUT)
 *     gpuAttributesCount: (INPUT)
 *         Attributes for any of the GPUs that require them. Some GPUs may be
 *         ommitted from this list, and in fact, the list can be empty
 *         (gpuAttributesCount = 0).
 *
 *     wddmVA: (INPUT)
 *         This is the address of the Microsoft allocation.
 *
 * Errors:
 *     See UvmMapRmAllocation.
 */
NV_STATUS UvmMapWddmAllocation(void               *base,
                               NvLength            length,
                               UvmProcessorUuid   *homeProcessorUuid,
                               UvmGpuAttributes   *perGpuAttributes,
                               NvLength            gpuAttributesCount,
                               void*               wddmVA);

/*******************************************************************************
 * UvmPreventMigrationRangeGroups
 *
 * This function first migrates as necessary, in order to bring all pages in the
 * range group to their home locations, if the home locations were set. Home
 * locations can be set via UvmAlloc or UvmMigrateRangeGroup.
 *
 * Then, it marks the range group as "not migratable". It leaves the individual
 * pages' migratable bit unchanged. This means that this call disables page
 * migration, except in certain cases (such as forced migration; please see:
 * UvmMigrateRangeGroup).
 *
 * This is reference counted: you must make the same number of calls to
 * UvmAllowMigration, as you made to UvmPreventMigration, in order to get back
 * to the original state.
 *
 * As for pages' home locations, note that:
 *
 * 1) Each page has a home location, and pages within a given range group may
 * have home locations that differ from each other.
 *
 * 2) If a page in the range group does not yet have any physical backing, then
 * this situation is left unchanged by this API call.
 *
 * 3) If the page's home location is unset, then the page is not migrated by
 * this call.
 *
 * Other GPUs are allowed to map, but not to migrate, the resulting memory.
 *
 * Existing CPU mappings are revoked, even if the pages are in system memory.
 * The CPU is *not* allowed to map the resulting memory on fault even if the
 * memory is located in sysmem. In other words, the CPU will always get a fatal
 * fault on accesses to any pages in this range group until
 * UvmAllowMigrationRangeGroups is called on this range group.
 *
 * MPS mode notes:
 *     In MPS mode, either client or server can call this function.
 *
 *     All processes in the same MPS group (all clients, and the server process)
 *     will lose their CPU mappings of all allocations in all of the range
 *     groups.
 *
 * Arguments:
 *     rangeGroupIds: (INPUT)
 *         An array of range group IDs.
 *
 *     numberOfGroupIds: (INPUT)
 *         Number of items in the rangeGroupIds array.
 *
 * Errors:
 *     NV_ERR_OBJECT_NOT_FOUND:
 *         One or more rangeGroupIds was not found.
 *
 *      NV_ERR_INVALID_ARGUMENT:
 *          A NULL pointer was passed in for rangeGroupIds. Or, numberOfGroupIds
 *          was zero.
 */
NV_STATUS UvmPreventMigrationRangeGroups(UvmRangeGroupId  *rangeGroupIds,
                                        NvLength         numberOfGroupIds);

/*******************************************************************************
 * UvmPreventMigration
 *
 * TODO: If we allow UvmPreventMigration to be called on any system memory
 * region (for example, on a region that was allocated via malloc), would this
 * make it too difficult to implement on Windows?
 *
 * This function first migrates as necessary, in order to bring all pages in the
 * base+length to the destinationProcessorUuid. Then, it marks the pages as "not
 * migratable". This means that:
 *
 *     1) page faults will not cause any of these pages to be migrated.
 *
 *     2) On a page fault for an address that falls within this range, a
 *         mapping will be created if:
 *
 *         a) it is possible to create a mapping from the faulting processor to
 *         the actual page location, and
 *
 *         b) The range is under the constraints of "UVM Coherence". See the top
 *         of this file for further information about UVM Coherence.
 *
 *       otherwise, the page fault is fatal.
 *
 * If any page in the base+length range has a home location that is different
 * from the destinationProcessorUuid, then this call will fail and return an
 * error.
 *
 * If any page in the base+length range is part of an external allocation (that
 * is, a range that was specified in a call to UvmMapRmAllocation or
 * UvmMapWddmAllocation), then this call will fail and return an error.
 *
 * If mapped, as specified by perGpuAttributes (via the
 * UVM_GPU_ATTRIBUTE_FLAGS_MAPPED flag), then mappings must persist at least
 * until UvmAllowMigration is called.
 *
 * Page mappings on the CPU: this routine does not make any guarantees about
 * whether or not it creates page mappings on the CPU. In order to create
 * page mappings on the CPU, the caller should call UvmPrefetch.
 *
 * MPS mode notes:
 *     In MPS mode, the server can call this function.
 *
 *     TODO: decide whether the client can call this function. This depends on
 *     cudaHostRegister implementation details. If the client call call this,
 *     then we need to choose a refcounting strategy for the migratability of
 *     the pages (virtual and physical may have different counts, or not).
 *
 * Arguments:
 *      base: (INPUT)
 *          Base address (starting point) of the VA (virtual address) range.
 *
 *      length: (INPUT)
 *          Length, in bytes, of the range.
 *
 *      perGpuAttributes: (INPUT)
 *      gpuAttributesCount: (INPUT)
 *          Attributes for any of the GPUs that require them. Some GPUs may be
 *          ommitted from this list, and in fact, the list can be empty
 *          (gpuAttributesCount = 0).
 *
 *      destinationProcessorUuid: (INPUT)
 *          UUID of the processor that the pages will be migrated to, or held at.
 *
 * Errors:
 *     NV_ERR_GPU_UUID_NOT_FOUND:
 *         The processor identified by destinationProcessorUuid was not found.
 *
 *         Or, one of the perGpuAttibutes elements contains a GPU UUID that was
 *         not found.
 *
 *     NV_ERR_INVALID_ARGUMENT:
 *         perGpuAttributes was NULL, but gpuAttributesCount was non-zero, OR
 *
 *     NV_ERR_INVALID_REQUEST:
 *         One or more pages in the base+length range has a home location that
 *         is different from the destinationProcessorUuid.
 */
NV_STATUS UvmPreventMigration(void               *base,
                              NvLength            length,
                              UvmGpuAttributes   *perGpuAttributes,
                              NvLength            gpuAttributesCount,
                              UvmProcessorUuid   *destinationProcessorUuid);

/*******************************************************************************
 * UvmAllowMigrationRangeGroups
 *
 * Undoes a call to UvmPreventMigrationRangeGroups.  These calls are reference
 * counted.
 *
 * MPS notes:
 *     In MPS mode, either a client or the server may call this routine.
 *
 * Arguments:
 *     rangeGroupIds: (INPUT)
 *         An array of range group IDs.
 *
 *     numberOfGroupIds: (INPUT)
 *         Number of items in the rangeGroupIds array.
 *
 * Errors:
 *     NV_ERR_OBJECT_NOT_FOUND:
 *         One or more rangeGroupIds was not found.
 *
 *      NV_ERR_INVALID_ARGUMENT:
 *         A NULL pointer was passed in for rangeGroupIds. Or, numberOfGroupIds
 *         was zero.
 */
NV_STATUS UvmAllowMigrationRangeGroups(UvmRangeGroupId  *rangeGroupIds,
                                       NvLength         numberOfGroupIds);

/*******************************************************************************
 * UvmAllowMigration
 *
 * Undoes a call to UvmPreventMigration.  These calls are reference counted.
 *
 * If, on the previous call to UvmPreventMigration, either
 * UVM_GPU_ATTRIBUTE_FLAGS_GPU_CACHEABLE or
 * UVM_GPU_ATTRIBUTE_FLAGS_ATOMICS_ENABLED was set, then this call will put the
 * pages back into a UVM-compatible coherence state.
 *
 * Arguments:
 *      base: (INPUT)
 *          Base address (starting point) of the VA (virtual address) range.
 *
 *      length: (INPUT)
 *          Length, in bytes, of the range.
 *
 */
NV_STATUS UvmAllowMigration(void      *base,
                            NvLength  length);

/*******************************************************************************
 * UvmFullRegisterMpsServer
 *
 * Registers the calling process as an MPS (Multi Process Service, a runtime
 * architecture designed to allow CUDA processes to share the same GPU hardware
 * context) server, and creates migration resources for the GPUs that are in
 * the gpuUuid argument.
 *
 * The UVM kernel module will create a unique handle and return it in the
 * serverId argument. This handle may be shared with other processes
 * (typically, MPS clients), for use when calling UvmRegisterMpsClient.
 *
 * This API call will be initially supported on Linux. Non-supported platforms
 * will receive an NV_ERR_NOT_SUPPORTED error as the return value, if they
 * call this function.
 *
 * --------------------------------------------
 * DISCUSSION OF MPS SERVER AND CLIENT BEHAVIOR
 * --------------------------------------------
 *
 * The server is a simple program that initialized itself, registers with the
 * UVM kernel driver, and then waits for client connections.
 *
 * There is one server for each "MPS group". An MPS Group is a collection of a
 * single server instance, and one or (usually) more clients. The MPS server and
 * its clients all have the same user ID (this is enforced by the kernel
 * driver).
 *
 * A client may only connect to one MPS server, ever, in its process lifetime.
 * However, there may be multiple servers running at the same time, as long as
 * they do not share any client instances.
 *
 * The server's role is to set up allocations and prepare for general CUDA
 * operation. After that, the clients should be able to run efficiently.
 * "Efficiently" in this case implies that the clients avoid RPC calls to the
 * server, where possible, and instead operate directly on pre-allocated memory
 * areas. In fact, ideally, most calls should be handled entirely on the client
 * side, without communication with the server.
 *
 * All allocations are owned by the server. Client allocations are forwarded,
 * via RPC call, to the server. If a client process calls UvmAlloc, then
 * UvmAlloc will return an error.
 *
 * Any range group that a server or any of its clients creates, will be assigned
 * a unique rangeGroupId (unique for that MPS group). Please see
 * UvmCreateRangeGroup for further information about that API call.
 *
 * If a server receives a CPU page fault in a region that the UVM driver
 * handles, then the server process will receive a SEGFAULT signal.
 *
 * The way that the server sets up allocations for use in the MPS system is as
 * follows:
 *
 *      a) Client sends an IPC (interprocess communications) call to the server,
 *      requesting an allocation. It passes in allocation size, and home GPU, as
 *      inputs.
 *
 *      b) Server calls the CUDA API: mallocManaged. This ultimately leads to an
 *      mmap(..., uvmFd) call on the memory range.
 *
 *      c) Because the region has been mmap'd to the uvmFd (UVM file
 *      descriptor), this keeps HMM (Heterogeneous Memory Management, a change
 *      to the core Linux kernel, that may not always be available in all
 *      kernels) out. In other words, either MPS or HMM will manage the memory
 *      region, but never both at the same time.
 *
 * EXAMPLE
 *
 * Here is an example in which a server and a client initialize, and then handle
 * a fault:
 *
 *    CLIENT                                   SERVER
 *    ------                                   ------
 *                                             UvmInitialize
 *                                             UvmRegisterMpsServer
 *                                             UvmReserveVA
 *                                             UvmRegisterGpuVaSpace
 *    UvmInitialize
 *    UvmRegisterMpsClient
 *    UvmReserveVA
 *
 * (App: cudaMallocManaged):
 *    UvmCreateRangeGroup
 *                                             UvmAlloc or UvmMemMap
 *    UvmMemMap
 *
 * (App: cudaFree):
 *    UvmFree
 *                                             UvmFree
 *
 * Arguments:
 *     gpuUuidArray: (INPUT)
 *         Pointer to an array of GPU UUIDs that will be managed by the MPS
 *         server.
 *
 * Error codes:
 *     NV_ERR_NOT_READY:
 *         This was called too early (before UvmInitialize, for example).
 *
 *     NV_ERR_NOT_SUPPORTED:
 *         This was called on a non-supported platform.
 *
 *     NV_ERR_NO_MEMORY:
 *         Internal memory allocation failed.
 *
 *     NV_ERR_INVALID_ARGUMENT:
 *         NULL gpuUuidArray, or other problem with the input arguments was
 *         detected.
 *
 *     NV_ERR_GENERIC:
 *         Unexpected error. We try hard to avoid returning this error code,
 *         because it is not very informative.
*/
NV_STATUS UvmFullRegisterMpsServer(NvU64 *serverId);

/*******************************************************************************
 * UvmUnregisterMpsServer
 *
 * Uregisters an MPS server that had been previously registered with a call to
 * UvmRegisterMpsServer.
*/
NV_STATUS UvmUnregisterMpsServer(NvU64 serverId);

/*******************************************************************************
    UvmRegisterMpsClient

    Registers the calling process as being an MPS (Multi Process Service, a
    runtime architecture designed to allow CUDA processes to share the same GPU
    hardware context) client allowed to use resources from the MPS server
    identified by serverId.

    This API call will be initially supported on Linux. Non-supported platforms
    will receive an NV_ERR_NOT_SUPPORTED error as the return value, if they
    call this function.

    Arguments:
        serverId: (INPUT)
            Unique identifier to identify the MPS server

    Error codes:
        NV_ERR_NOT_SUPPORTED
        NV_ERR_INVALID_ARGUMENT
        NV_ERR_INSUFFICIENT_PERMISSIONS
*/
NV_STATUS UvmRegisterMpsClient(NvU64 serverId);

/*******************************************************************************
    UvmUnregisterMpsClient

    Uregisters an MPS client that had been previously registered with a call to
    UvmRegisterMpsServer.
*/
NV_STATUS UvmUnregisterMpsClient(NvU64 clientId);

/*******************************************************************************
 * UvmGetFileDescriptor
 *
 * The file descriptor (of the type returned by open(2)) that refers to the UVM
 * character device (Unix) can be used to get notified when migration or fatal
 * events occur.
 *
 * Intrusive tests also benefit from being able to make ioctl calls directly
 * into the kernel driver. This routine provides the file descriptor that makes
 * that possible.
 *
 * Arguments:
 *  returnedFd: (OUTPUT)
 *      File descriptor (of the type returned by open(2)) that refers to the
 *      UVM character device (Unix) or Windows file handle (Windows). (The
 *      initial implementation is only on Linux.)
 *
 *      The file descriptor may be uninitialized, if the caller calls this
 *      routine before UvmInitialize. In that case, *returnedFd will be -1.
 *
 * Error codes:
 *  NV_ERR_INVALID_ARGUMENT
*/
NV_STATUS UvmGetFileDescriptor(int *returnedFd);

/*******************************************************************************
 * UvmIs8Supported
 *
 * Argument:
 *  is8SupportedOut: (OUTPUT)
 *      Will be set to true (nonzero) if the driver was loaded as UVM 8, or
 *      false (zero) if it was loaded as UVM Lite.
*/
NV_STATUS UvmIs8Supported(NvU32 *is8SupportedOut);

#ifdef __cplusplus
}
#endif

#endif // _UVM_H_
