* PROJECT: ReactOS system libraries
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Encode/decode helpers for memory and port resource descriptors.
* COPYRIGHT: Copyright 2026 Justin Miller <justin.miller@reactos.org>
*/
#include <rtl.h>
#define NDEBUG
#include <debug.h>
* A plain Port/Memory descriptor stores its Length (and, for a requirement, its
* Alignment) in a 32-bit field, so it can only describe regions up to 4 GB. A
* CmResourceTypeMemoryLarge descriptor reuses that same 32-bit field to hold a
* bigger value by dropping the low bits that are known to be zero; the Flags say
* how many were dropped:
*
* CM_RESOURCE_MEMORY_LARGE_40 (0x200) field holds bits [39:8] (<< 8)
* CM_RESOURCE_MEMORY_LARGE_48 (0x400) field holds bits [47:16] (<< 16)
* CM_RESOURCE_MEMORY_LARGE_64 (0x800) field holds bits [63:32] (<< 32)
*/
static
ULONGLONG
RtlpDecodeMemIoValue(
_In_ UCHAR Type,
_In_ USHORT Flags,
_In_ ULONG Encoded)
{
if (Type == CmResourceTypePort || Type == CmResourceTypeMemory)
return Encoded;
if (Flags & CM_RESOURCE_MEMORY_LARGE_40)
return (ULONGLONG)Encoded << 8;
if (Flags & CM_RESOURCE_MEMORY_LARGE_48)
return (ULONGLONG)Encoded << 16;
if (Flags & CM_RESOURCE_MEMORY_LARGE_64)
return (ULONGLONG)Encoded << 32;
return 0;
}
* @implemented
*
* Decode an assigned (CM_PARTIAL_RESOURCE_DESCRIPTOR) memory or port descriptor:
* return its full 64-bit length and, optionally, its start address.
*/
ULONGLONG
NTAPI
RtlCmDecodeMemIoResource(
_In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
_Out_opt_ PULONGLONG Start)
{
if (Start != NULL)
*Start = Descriptor->u.Generic.Start.QuadPart;
return RtlpDecodeMemIoValue(Descriptor->Type,
Descriptor->Flags,
Descriptor->u.Generic.Length);
}
* @implemented
*
* Decode a requirement (IO_RESOURCE_DESCRIPTOR) memory or port descriptor:
* return its full 64-bit length, maybe its (also large-encoded)
* alignment and the 64-bit minimum/maximum address bounds.
*/
ULONGLONG
NTAPI
RtlIoDecodeMemIoResource(
_In_ PIO_RESOURCE_DESCRIPTOR Descriptor,
_Out_opt_ PULONGLONG Alignment,
_Out_opt_ PULONGLONG MinimumAddress,
_Out_opt_ PULONGLONG MaximumAddress)
{
if (Alignment != NULL)
{
*Alignment = RtlpDecodeMemIoValue(Descriptor->Type,
Descriptor->Flags,
Descriptor->u.Generic.Alignment);
}
if (MinimumAddress != NULL)
*MinimumAddress = Descriptor->u.Generic.MinimumAddress.QuadPart;
if (MaximumAddress != NULL)
*MaximumAddress = Descriptor->u.Generic.MaximumAddress.QuadPart;
return RtlpDecodeMemIoValue(Descriptor->Type,
Descriptor->Flags,
Descriptor->u.Generic.Length);
}
* @implemented
*
* Fill in a CM_PARTIAL_RESOURCE_DESCRIPTOR for a port or memory region. When a
* memory Length does not fit in 32 bits the descriptor is promoted to a
* CmResourceTypeMemoryLarge form.
*
* Returns STATUS_INVALID_PARAMETER for an unsupported Type (or a port length
* above 4 GB) and STATUS_UNSUCCESSFUL if the length cannot be represented by any
* large form without dropping bits.
*/
NTSTATUS
NTAPI
RtlCmEncodeMemIoResource(
_In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
_In_ UCHAR Type,
_In_ ULONGLONG Length,
_In_ ULONGLONG Start)
{
if (Type != CmResourceTypePort &&
Type != CmResourceTypeMemory &&
Type != CmResourceTypeMemoryLarge)
{
return STATUS_INVALID_PARAMETER;
}
if (Type == CmResourceTypePort)
{
if (Length > MAXULONG)
return STATUS_INVALID_PARAMETER;
Descriptor->Type = CmResourceTypePort;
Descriptor->u.Generic.Start.QuadPart = Start;
Descriptor->u.Generic.Length = (ULONG)Length;
return STATUS_SUCCESS;
}
Descriptor->Flags &= ~CM_RESOURCE_MEMORY_LARGE;
Descriptor->u.Generic.Start.QuadPart = Start;
if (Length <= MAXULONG)
{
Descriptor->Type = CmResourceTypeMemory;
Descriptor->u.Generic.Length = (ULONG)Length;
return STATUS_SUCCESS;
}
if (Length <= ((ULONGLONG)MAXULONG << 8) && (Length & 0xFF) == 0)
{
Descriptor->u.Generic.Length = (ULONG)(Length >> 8);
Descriptor->Flags |= CM_RESOURCE_MEMORY_LARGE_40;
}
else if (Length <= ((ULONGLONG)MAXULONG << 16) && (Length & 0xFFFF) == 0)
{
Descriptor->u.Generic.Length = (ULONG)(Length >> 16);
Descriptor->Flags |= CM_RESOURCE_MEMORY_LARGE_48;
}
else if (Length <= ((ULONGLONG)MAXULONG << 32) && (Length & 0xFFFFFFFF) == 0)
{
Descriptor->u.Generic.Length = (ULONG)(Length >> 32);
Descriptor->Flags |= CM_RESOURCE_MEMORY_LARGE_64;
}
else
{
return STATUS_UNSUCCESSFUL;
}
Descriptor->Type = CmResourceTypeMemoryLarge;
return STATUS_SUCCESS;
}
* A large descriptor expresses alignment at the same granularity as its length.
* Scale the alignment up until it is a multiple of that granularity.
*/
static
BOOLEAN
RtlpEncodeLargeAlignment(
_In_ ULONGLONG Alignment,
_In_ ULONG Shift,
_Out_ PULONG Encoded)
{
ULONGLONG LowMask = ((ULONGLONG)1 << Shift) - 1;
while ((Alignment & LowMask) != 0)
{
ULONGLONG Doubled = Alignment << 1;
if (Doubled < Alignment)
return FALSE;
Alignment = Doubled;
}
*Encoded = (ULONG)(Alignment >> Shift);
return TRUE;
}
* @implemented
*
* Fill in an IO_RESOURCE_DESCRIPTOR (a resource *requirement*) for a port or
* memory range. Also encodes the alignment and the 64-bit address bounds.
* A memory length above 4 GB is promoted to a CmResourceTypeMemoryLarge form,
* which matters as drivers depend on this behavior.
*/
NTSTATUS
NTAPI
RtlIoEncodeMemIoResource(
_In_ PIO_RESOURCE_DESCRIPTOR Descriptor,
_In_ UCHAR Type,
_In_ ULONGLONG Length,
_In_ ULONGLONG Alignment,
_In_ ULONGLONG MinimumAddress,
_In_ ULONGLONG MaximumAddress)
{
ULONG Shift;
USHORT LargeFlag;
if (Type != CmResourceTypePort &&
Type != CmResourceTypeMemory &&
Type != CmResourceTypeMemoryLarge)
{
return STATUS_INVALID_PARAMETER;
}
if (Type == CmResourceTypePort)
{
if (Length > MAXULONG || Alignment > MAXULONG)
return STATUS_INVALID_PARAMETER;
Descriptor->Type = CmResourceTypePort;
Descriptor->u.Generic.MinimumAddress.QuadPart = MinimumAddress;
Descriptor->u.Generic.MaximumAddress.QuadPart = MaximumAddress;
Descriptor->u.Generic.Length = (ULONG)Length;
Descriptor->u.Generic.Alignment = (ULONG)Alignment;
return STATUS_SUCCESS;
}
Descriptor->Flags &= ~CM_RESOURCE_MEMORY_LARGE;
Descriptor->u.Generic.MinimumAddress.QuadPart = MinimumAddress;
Descriptor->u.Generic.MaximumAddress.QuadPart = MaximumAddress;
if (Length <= MAXULONG && Alignment <= MAXULONG)
{
Descriptor->Type = CmResourceTypeMemory;
Descriptor->u.Generic.Length = (ULONG)Length;
Descriptor->u.Generic.Alignment = (ULONG)Alignment;
return STATUS_SUCCESS;
}
if (Length <= ((ULONGLONG)MAXULONG << 8))
{
if (Alignment > ((ULONGLONG)MAXULONG << 8))
return STATUS_UNSUCCESSFUL;
Shift = 8;
LargeFlag = CM_RESOURCE_MEMORY_LARGE_40;
}
else if (Length <= ((ULONGLONG)MAXULONG << 16))
{
if (Alignment > ((ULONGLONG)MAXULONG << 16))
return STATUS_UNSUCCESSFUL;
Shift = 16;
LargeFlag = CM_RESOURCE_MEMORY_LARGE_48;
}
else if (Length <= ((ULONGLONG)MAXULONG << 32))
{
Shift = 32;
LargeFlag = CM_RESOURCE_MEMORY_LARGE_64;
}
else
{
return STATUS_UNSUCCESSFUL;
}
if ((Length & (((ULONGLONG)1 << Shift) - 1)) != 0)
return STATUS_UNSUCCESSFUL;
if (!RtlpEncodeLargeAlignment(Alignment, Shift, &Descriptor->u.Generic.Alignment))
return STATUS_UNSUCCESSFUL;
Descriptor->Type = CmResourceTypeMemoryLarge;
Descriptor->u.Generic.Length = (ULONG)(Length >> Shift);
Descriptor->Flags |= LargeFlag;
return STATUS_SUCCESS;
}
* @implemented
*
* Round a requested length up to the nearest value the descriptor
* 1:1 when it fits 32 bits, otherwise the next multiple of the
* granularity.
*/
NTSTATUS
NTAPI
RtlFindClosestEncodableLength(
_In_ ULONGLONG SourceLength,
_Out_ PULONGLONG TargetLength)
{
if (SourceLength <= MAXULONG)
*TargetLength = SourceLength;
else if (SourceLength <= ((ULONGLONG)MAXULONG << 8))
*TargetLength = (SourceLength + 0xFF) & ~(ULONGLONG)0xFF;
else if (SourceLength <= ((ULONGLONG)MAXULONG << 16))
*TargetLength = (SourceLength + 0xFFFF) & ~(ULONGLONG)0xFFFF;
else if (SourceLength <= ((ULONGLONG)MAXULONG << 32))
*TargetLength = (SourceLength + 0xFFFFFFFF) & ~(ULONGLONG)0xFFFFFFFF;
else
{
*TargetLength = 0;
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}