/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    semphore.c

Abstract:

   This module implements the executive semaphore object. Functions are
   provided to create, open, release, and query semaphore objects.

Author:

    David N. Cutler (davec) 8-May-1989

Environment:

    Kernel mode only.

Revision History:

--*/

#include "exp.h"

//
// Temporary so boost is patchable
//

ULONG ExpSemaphoreBoost = SEMAPHORE_INCREMENT;

//
// Address of semaphore object type descriptor.
//

POBJECT_TYPE ExSemaphoreObjectType;

//
// Structure that describes the mapping of generic access rights to object
// specific access rights for semaphore objects.
//

#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("INITCONST")
#endif
const GENERIC_MAPPING ExpSemaphoreMapping = {
    STANDARD_RIGHTS_READ |
        SEMAPHORE_QUERY_STATE,
    STANDARD_RIGHTS_WRITE |
        SEMAPHORE_MODIFY_STATE,
    STANDARD_RIGHTS_EXECUTE |
        SYNCHRONIZE,
    SEMAPHORE_ALL_ACCESS
};
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, ExpSemaphoreInitialization)
#pragma alloc_text(PAGE, NtCreateSemaphore)
#pragma alloc_text(PAGE, NtOpenSemaphore)
#pragma alloc_text(PAGE, NtQuerySemaphore)
#pragma alloc_text(PAGE, NtReleaseSemaphore)
#endif

BOOLEAN
ExpSemaphoreInitialization (
    )

/*++

Routine Description:

    This function creates the semaphore object type descriptor at system
    initialization and stores the address of the object type descriptor
    in local static storage.

Arguments:

    None.

Return Value:

    A value of TRUE is returned if the semaphore object type descriptor is
    successfully created. Otherwise a value of FALSE is returned.

--*/

{

    OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
    NTSTATUS Status;
    UNICODE_STRING TypeName;

    //
    // Initialize string descriptor.
    //

    RtlInitUnicodeString(&TypeName, L"Semaphore");

    //
    // Create semaphore object type descriptor.
    //

    RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
    ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
    ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
    ObjectTypeInitializer.GenericMapping = ExpSemaphoreMapping;
    ObjectTypeInitializer.PoolType = NonPagedPool;
    ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KSEMAPHORE);
    ObjectTypeInitializer.ValidAccessMask = SEMAPHORE_ALL_ACCESS;
    Status = ObCreateObjectType(&TypeName,
                                &ObjectTypeInitializer,
                                (PSECURITY_DESCRIPTOR)NULL,
                                &ExSemaphoreObjectType);

    //
    // If the semaphore object type descriptor was successfully created, then
    // return a value of TRUE. Otherwise return a value of FALSE.
    //

    return (BOOLEAN)(NT_SUCCESS(Status));
}

NTSTATUS
NtCreateSemaphore (
    IN PHANDLE SemaphoreHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
    IN LONG InitialCount,
    IN LONG MaximumCount
    )

/*++

Routine Description:

    This function creates a semaphore object, sets its initial count to the
    specified value, sets its maximum count to the specified value, and opens
    a handle to the object with the specified desired access.

Arguments:

    SemaphoreHandle - Supplies a pointer to a variable that will receive the
        semaphore object handle.

    DesiredAccess - Supplies the desired types of access for the semaphore
        object.

    ObjectAttributes - Supplies a pointer to an object attributes structure.

    InitialCount - Supplies the initial count of the semaphore object.

    MaximumCount - Supplies the maximum count of the semaphore object.

Return Value:

    NTSTATUS.

--*/

{

    HANDLE Handle;
    KPROCESSOR_MODE PreviousMode;
    PVOID Semaphore;
    NTSTATUS Status;

    //
    // Establish an exception handler, probe the output handle address, and
    // attempt to create a semaphore object. If the probe fails, then return
    // the exception code as the service status. Otherwise return the status
    // value returned by the object insertion routine.
    //
    // Get previous processor mode and probe output handle address if
    // necessary.
    //

    PreviousMode = KeGetPreviousMode();
    if (PreviousMode != KernelMode) {
        try {
            ProbeForWriteHandle(SemaphoreHandle);
        } except(ExSystemExceptionFilter()) {
            return GetExceptionCode();
        }
    }

    //
    // Check argument validity.
    //

    if ((MaximumCount <= 0) || (InitialCount < 0) ||
       (InitialCount > MaximumCount)) {
        return STATUS_INVALID_PARAMETER;
    }

    //
    // Allocate semaphore object.
    //

    Status = ObCreateObject(PreviousMode,
                            ExSemaphoreObjectType,
                            ObjectAttributes,
                            PreviousMode,
                            NULL,
                            sizeof(KSEMAPHORE),
                            0,
                            0,
                            (PVOID *)&Semaphore);

    //
    // If the semaphore object was successfully allocated, then initialize
    // the semaphore object and attempt to insert the semaphore object in
    // the current process' handle table.
    //

    if (NT_SUCCESS(Status)) {
        KeInitializeSemaphore((PKSEMAPHORE)Semaphore,
                              InitialCount,
                              MaximumCount);

        Status = ObInsertObject(Semaphore,
                                NULL,
                                DesiredAccess,
                                0,
                                (PVOID *)NULL,
                                &Handle);

        //
        // If the semaphore object was successfully inserted in the current
        // process' handle table, then attempt to write the semaphore handle
        // value. If the write attempt fails, then do not report an error.
        // When the caller attempts to access the handle value, an access
        // violation will occur.
        //

        if (NT_SUCCESS(Status)) {
            if (PreviousMode != KernelMode) {
                try {
                    *SemaphoreHandle = Handle;
                } except(ExSystemExceptionFilter()) {
                    NOTHING;
                }
            }
            else {
                *SemaphoreHandle = Handle;
            }
        }
    }

    //
    // Return service status.
    //

    return Status;
}

NTSTATUS
NtOpenSemaphore (
    OUT PHANDLE SemaphoreHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes
    )

/*++

Routine Description:

    This function opens a handle to a semaphore object with the specified
    desired access.

Arguments:

    SemaphoreHandle - Supplies a pointer to a variable that will receive the
        semaphore object handle.

    DesiredAccess - Supplies the desired types of access for the semaphore
        object.

    ObjectAttributes - Supplies a pointer to an object attributes structure.

Return Value:

    NTSTATUS.

--*/

{

    HANDLE Handle;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status;


    //
    // Establish an exception handler, probe the output handle address, and
    // attempt to open a semaphore object. If the probe fails, then return
    // the exception code as the service status. Otherwise return the status
    // value returned by the object open routine.
    //
    // Get previous processor mode and probe output handle address if
    // necessary.
    //

    PreviousMode = KeGetPreviousMode();
    if (PreviousMode != KernelMode) {
        try {
            ProbeForWriteHandle(SemaphoreHandle);
        } except(ExSystemExceptionFilter()) {
            return GetExceptionCode();
        }
    }

    //
    // Open handle to the semaphore object with the specified desired access.
    //

    Status = ObOpenObjectByName(ObjectAttributes,
                                ExSemaphoreObjectType,
                                PreviousMode,
                                NULL,
                                DesiredAccess,
                                NULL,
                                &Handle);

    //
    // If the open was successful, then attempt to write the semaphore
    // object handle value. If the write attempt fails, then do not report
    // an error. When the caller attempts to access the handle value, an
    // access violation will occur.
    //

    if (NT_SUCCESS(Status)) {
        if (PreviousMode != KernelMode) {
            try {
                *SemaphoreHandle = Handle;
            } except(ExSystemExceptionFilter()) {
                NOTHING;
            }
        }
        else {
            *SemaphoreHandle = Handle;
        }
    }

    //
    // Return service status.
    //

    return Status;
}

NTSTATUS
NtQuerySemaphore (
    IN HANDLE SemaphoreHandle,
    IN SEMAPHORE_INFORMATION_CLASS SemaphoreInformationClass,
    OUT PVOID SemaphoreInformation,
    IN ULONG SemaphoreInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    )

/*++

Routine Description:

    This function queries the state of a semaphore object and returns the
    requested information in the specified record structure.

Arguments:

    SemaphoreHandle - Supplies a handle to a semaphore object.

    SemaphoreInformationClass - Supplies the class of information being
        requested.

    SemaphoreInformation - Supplies a pointer to a record that is to receive
        the requested information.

    SemaphoreInformationLength - Supplies the length of the record that is
        to receive the requested information.

    ReturnLength - Supplies an optional pointer to a variable that will
        receive the actual length of the information that is returned.

Return Value:

    NTSTATUS.

--*/

{

    PVOID Semaphore;
    LONG Count;
    LONG Maximum;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status;

    //
    // Establish an exception handler, probe the output arguments, reference
    // the semaphore object, and return the specified information. If the probe
    // fails, then return the exception code as the service status. Otherwise
    // return the status value returned by the reference object by handle
    // routine.
    //
    // Get previous processor mode and probe output arguments if necessary.
    //

    PreviousMode = KeGetPreviousMode();
    if (PreviousMode != KernelMode) {
        try {
            ProbeForWriteSmallStructure (SemaphoreInformation,
                                         sizeof(SEMAPHORE_BASIC_INFORMATION),
                                         sizeof(ULONG));

            if (ARGUMENT_PRESENT(ReturnLength)) {
                ProbeForWriteUlong(ReturnLength);
            }
        } except(ExSystemExceptionFilter()) {
            return GetExceptionCode();
        }
    }

    //
    // Check argument validity.
    //

    if (SemaphoreInformationClass != SemaphoreBasicInformation) {
        return STATUS_INVALID_INFO_CLASS;
    }

    if (SemaphoreInformationLength != sizeof(SEMAPHORE_BASIC_INFORMATION)) {
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    //
    // Reference semaphore object by handle.
    //

    Status = ObReferenceObjectByHandle(SemaphoreHandle,
                                       SEMAPHORE_QUERY_STATE,
                                       ExSemaphoreObjectType,
                                       PreviousMode,
                                       &Semaphore,
                                       NULL);

    //
    // If the reference was successful, then read the current state and
    // maximum count of the semaphore object, dereference semaphore object,
    // fill in the information structure, and return the length of the
    // information structure if specified. If the write of the semaphore
    // information or the return length fails, then do not report an error.
    // When the caller accesses the information structure or length an
    // access violation will occur.
    //

    if (NT_SUCCESS(Status)) {
        Count = KeReadStateSemaphore((PKSEMAPHORE)Semaphore);
        Maximum = ((PKSEMAPHORE)Semaphore)->Limit;
        ObDereferenceObject(Semaphore);

        if (PreviousMode != KernelMode) {
            try {
                ((PSEMAPHORE_BASIC_INFORMATION)SemaphoreInformation)->CurrentCount = Count;
                ((PSEMAPHORE_BASIC_INFORMATION)SemaphoreInformation)->MaximumCount = Maximum;
                if (ARGUMENT_PRESENT(ReturnLength)) {
                    *ReturnLength = sizeof(SEMAPHORE_BASIC_INFORMATION);
                }
            } except(ExSystemExceptionFilter()) {
                NOTHING;
            }
        }
        else {
            ((PSEMAPHORE_BASIC_INFORMATION)SemaphoreInformation)->CurrentCount = Count;
            ((PSEMAPHORE_BASIC_INFORMATION)SemaphoreInformation)->MaximumCount = Maximum;
            if (ARGUMENT_PRESENT(ReturnLength)) {
                *ReturnLength = sizeof(SEMAPHORE_BASIC_INFORMATION);
            }
        }
    }

    //
    // Return service status.
    //

    return Status;
}

NTSTATUS
NtReleaseSemaphore (
    IN HANDLE SemaphoreHandle,
    IN LONG ReleaseCount,
    OUT PLONG PreviousCount OPTIONAL
    )

/*++

Routine Description:

    This function releases a semaphore object by adding the specified release
    count to the current value.

Arguments:

    Semaphore - Supplies a handle to a semaphore object.

    ReleaseCount - Supplies the release count that is to be added to the
        current semaphore count.

    PreviousCount - Supplies an optional pointer to a variable that will
        receive the previous semaphore count.

Return Value:

    NTSTATUS.

--*/

{

    LONG Count;
    PVOID Semaphore;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status;

    //
    // Establish an exception handler, probe the previous count address if
    // specified, reference the semaphore object, and release the semaphore
    // object. If the probe fails, then return the exception code as the
    // service status. Otherwise return the status value returned by the
    // reference object by handle routine.
    //
    // Get previous processor mode and probe previous count address
    // if necessary.
    //

    PreviousMode = KeGetPreviousMode();

    if ((ARGUMENT_PRESENT(PreviousCount)) && (PreviousMode != KernelMode)) {
        try {
            ProbeForWriteLong(PreviousCount);
        } except(ExSystemExceptionFilter()) {
            return GetExceptionCode();
        }
    }

    //
    // Check argument validity.
    //

    if (ReleaseCount <= 0) {
        return STATUS_INVALID_PARAMETER;
    }

    //
    // Reference semaphore object by handle.
    //

    Status = ObReferenceObjectByHandle(SemaphoreHandle,
                                       SEMAPHORE_MODIFY_STATE,
                                       ExSemaphoreObjectType,
                                       PreviousMode,
                                       &Semaphore,
                                       NULL);

    //
    // If the reference was successful, then release the semaphore object.
    // If an exception occurs because the maximum count of the semaphore
    // has been exceeded, then dereference the semaphore object and return
    // the exception code as the service status. Otherwise write the previous
    // count value if specified. If the write of the previous count fails,
    // then do not report an error. When the caller attempts to access the
    // previous count value, an access violation will occur.
    //

    if (NT_SUCCESS(Status)) {

        //
        // Initialize Count to keep W4 compiler happy.
        //

        Count = 0;

        try {
            PERFINFO_DECLARE_OBJECT(Semaphore);

            Count = KeReleaseSemaphore((PKSEMAPHORE)Semaphore,
                                       ExpSemaphoreBoost,
                                       ReleaseCount,
                                       FALSE);

        } except(ExSystemExceptionFilter()) {
            Status = GetExceptionCode();
        }

        ObDereferenceObject(Semaphore);

        if (NT_SUCCESS(Status) && ARGUMENT_PRESENT(PreviousCount)) {
            if (PreviousMode != KernelMode) {
                try {
                    *PreviousCount = Count;
                } except(ExSystemExceptionFilter()) {
                    NOTHING;
                }
            }
            else {
                *PreviousCount = Count;
            }
        }
    }

    //
    // Return service status.
    //

    return Status;
}
