[BOOTLIB]: Clarify some attributes now that their meaning is clearer.
[reactos.git] / reactos / boot / environ / lib / mm / pagealloc.c
index 1ca2652..ffe2f6e 100644 (file)
@@ -24,6 +24,8 @@ typedef struct _BL_PA_REQUEST
 
 /* DATA VARIABLES ************************************************************/
 
+extern ULONG MmArchLargePageSize;
+
 ULONGLONG PapMaximumPhysicalPage, PapMinimumPhysicalPage;
 
 ULONG PapMinimumAllocationCount;
@@ -95,7 +97,7 @@ MmPapAllocateRegionFromMdl (
     ULONGLONG LocalEndPage, FoundEndPage, LocalVirtualEndPage;
 
     /* Check if any parameters were not passed in correctly */
-    if ( !(CurrentList) || !(Request) || (!(NewList) && !(Descriptor)))
+    if (!(CurrentList) || !(Request) || (!(NewList) && !(Descriptor)))
     {
         return STATUS_INVALID_PARAMETER;
     }
@@ -188,7 +190,8 @@ MmPapAllocateRegionFromMdl (
     /* Are we allocating from the virtual memory list? */
     if (CurrentList == &MmMdlMappedUnallocated)
     {
-        EfiPrintf(L"Virtual memory not yet supported\r\n");
+        EfiPrintf(L"not yet implemented in %S\r\n", __FUNCTION__);
+        EfiStall(1000000);
         return STATUS_NOT_IMPLEMENTED;
     }
 
@@ -297,8 +300,15 @@ MmPaAllocatePages (
         return Status;
     }
 
-    /* Nope, we have to hunt for it elsewhere */
-    EfiPrintf(L"TODO\r\n");
+    /* Are we failing due to some attributes? */
+    if (Request->Flags & BlMemoryValidAllocationAttributeMask)
+    {
+        EfiPrintf(L"not yet implemented in %S\r\n", __FUNCTION__);
+        EfiStall(1000000);
+        return STATUS_NOT_IMPLEMENTED;
+    }
+
+    /* Nope, just fail the entire call */
     return Status;
 }
 
@@ -410,6 +420,168 @@ Quickie:
     return Status;
 }
 
+NTSTATUS
+MmPapPageAllocatorExtend (
+    _In_ ULONG Attributes,
+    _In_ ULONG Alignment,
+    _In_ ULONGLONG PageCount,
+    _In_ ULONGLONG VirtualPage,
+    _In_opt_ PBL_ADDRESS_RANGE Range,
+    _In_opt_ ULONG Type
+    )
+{
+    BL_PA_REQUEST Request;
+    ULONGLONG PageRange;
+    BL_MEMORY_DESCRIPTOR NewDescriptor;
+    ULONG AllocationFlags, CacheAttributes, AddFlags;
+    NTSTATUS Status;
+    PBL_MEMORY_DESCRIPTOR_LIST MdList;
+    PBL_MEMORY_DESCRIPTOR Descriptor;
+    PVOID VirtualAddress;
+    PHYSICAL_ADDRESS PhysicalAddress;
+
+    /* Is the caller requesting less pages than allowed? */
+    if (!(Attributes & BlMemoryFixed) &&
+        !(Range) &&
+        (PageCount < PapMinimumAllocationCount))
+    {
+        /* Unless this is a fixed request, then adjust the original requirements */
+        PageCount = PapMinimumAllocationCount;
+        Alignment = PapMinimumAllocationCount;
+    }
+
+    /* Extract only the allocation attributes */
+    AllocationFlags = Attributes & BlMemoryValidAllocationAttributeMask;
+
+    /* Check if the caller wants large pages */
+    if ((AllocationFlags & BlMemoryLargePages) && (MmArchLargePageSize != 1))
+    {
+        EfiPrintf(L"Large pages not supported!\r\n");
+        EfiStall(10000000);
+        return STATUS_NOT_IMPLEMENTED;
+    }
+
+    /* Set an emty virtual range */
+    Request.VirtualRange.Minimum = 0;
+    Request.VirtualRange.Maximum = 0;
+
+    /* Check if the caller requested a range */
+    if (Range)
+    {
+        /* Calculate it size in pages, minus a page as this is a 0-based range */
+        PageRange = ((Range->Maximum - Range->Minimum) >> PAGE_SHIFT) - 1;
+
+        /* Set the minimum and maximum, in pages */
+        Request.BaseRange.Minimum = Range->Minimum >> PAGE_SHIFT;
+        Request.BaseRange.Maximum = Request.BaseRange.Minimum + PageRange;
+    }
+    else
+    {
+        /* Initialize a range from the smallest page to the biggest */
+        Request.BaseRange.Minimum = PapMinimumPhysicalPage;
+        Request.BaseRange.Maximum = 0xFFFFFFFF / PAGE_SIZE;
+    }
+
+    /* Get the cache attributes */
+    CacheAttributes = Attributes & BlMemoryValidCacheAttributeMask;
+
+    /* Check if the caller requested a valid allocation type */
+    if ((Type) && !(Type & ~(BL_MM_REQUEST_DEFAULT_TYPE |
+                             BL_MM_REQUEST_TOP_DOWN_TYPE)))
+    {
+        /* Use what the caller wanted */
+        Request.Type = Type;
+    }
+    else
+    {
+        /* Use the default bottom-up type */
+        Request.Type = BL_MM_REQUEST_DEFAULT_TYPE;
+    }
+
+    /* Use the original protection and type, but ignore other attributes */
+    Request.Flags = Attributes & ~(BlMemoryValidAllocationAttributeMask |
+                                   BlMemoryValidCacheAttributeMask);
+    Request.Alignment = Alignment;
+    Request.Pages = PageCount;
+
+    /* Allocate some free pages */
+    Status = MmPaAllocatePages(NULL,
+                               &NewDescriptor,
+                               &MmMdlUnmappedUnallocated,
+                               &Request,
+                               BlConventionalMemory);
+    if (!NT_SUCCESS(Status))
+    {
+        EfiPrintf(L"Failed to get unmapped,unallocated memory!\r\n");
+        EfiStall(10000000);
+        return Status;
+    }
+
+    /* Initialize a descriptor for these pages, adding in the allocation flags */
+    Descriptor = MmMdInitByteGranularDescriptor(AllocationFlags |
+                                                NewDescriptor.Flags,
+                                                BlConventionalMemory,
+                                                NewDescriptor.BasePage,
+                                                NewDescriptor.VirtualPage,
+                                                NewDescriptor.PageCount);
+
+    /* Now map a virtual address for these physical pages */
+    VirtualAddress = (PVOID)((ULONG_PTR)VirtualPage << PAGE_SHIFT);
+    PhysicalAddress.QuadPart = NewDescriptor.BasePage << PAGE_SHIFT;
+    Status = BlMmMapPhysicalAddressEx(&VirtualAddress,
+                                      AllocationFlags | CacheAttributes,
+                                      NewDescriptor.PageCount << PAGE_SHIFT,
+                                      PhysicalAddress);
+    EfiPrintf(L"MAP status: %lx\r\n", Status);
+    if (Status == STATUS_SUCCESS)
+    {
+        /* Add the cache attributes now that the mapping worked */
+        Descriptor->Flags |= CacheAttributes;
+
+        /* Update the virtual page now that we mapped it */
+        Descriptor->VirtualPage = (ULONG_PTR)VirtualAddress >> PAGE_SHIFT;
+
+        /* Add this as a mapped region */
+        Status = MmMdAddDescriptorToList(&MmMdlMappedUnallocated,
+                                         Descriptor,
+                                         BL_MM_ADD_DESCRIPTOR_COALESCE_FLAG);
+
+        /* Make new descriptor that we'll add in firmware allocation tracker */
+        MdList = &MmMdlFwAllocationTracker;
+        Descriptor = MmMdInitByteGranularDescriptor(0,
+                                                    BlConventionalMemory,
+                                                    NewDescriptor.BasePage,
+                                                    0,
+                                                    NewDescriptor.PageCount);
+
+        /* Do not coalesce */
+        AddFlags = 0;
+    }
+    else
+    {
+        /* We failed, free the physical pages */
+        Status = MmFwFreePages(NewDescriptor.BasePage, NewDescriptor.PageCount);
+        if (!NT_SUCCESS(Status))
+        {
+            /* We failed to free the pages, so this is still around */
+            MdList = &MmMdlUnmappedAllocated;
+        }
+        else
+        {
+            /* This is now back to unmapped/unallocated memory */
+            Descriptor->Flags = 0;
+            MdList = &MmMdlUnmappedUnallocated;
+        }
+
+        /* Coalesce the free descriptor */
+        AddFlags = BL_MM_ADD_DESCRIPTOR_COALESCE_FLAG;
+    }
+
+    /* Either add to firmware list, or to unmapped list, then return result */
+    MmMdAddDescriptorToList(MdList, Descriptor, AddFlags);
+    return Status;
+}
+
 NTSTATUS
 MmPapAllocatePagesInRange (
     _Inout_ PVOID* PhysicalAddress,
@@ -423,10 +595,16 @@ MmPapAllocatePagesInRange (
 {
     NTSTATUS Status;
     PHYSICAL_ADDRESS BaseAddress;
+    BL_PA_REQUEST Request;
+    PBL_MEMORY_DESCRIPTOR_LIST List;
+    BL_MEMORY_DESCRIPTOR Descriptor;
 
     /* Increment nesting depth */
     ++MmDescriptorCallTreeCount;
 
+    /* Default list */
+    List = &MmMdlMappedAllocated;
+
     /* Check for missing parameters or invalid range */
     if (!(PhysicalAddress) ||
         !(Pages) ||
@@ -439,14 +617,118 @@ MmPapAllocatePagesInRange (
     /* What translation mode are we using? */
     if (MmTranslationType != BlNone)
     {
-        /* We don't support virtual memory yet */
-        Status = STATUS_NOT_IMPLEMENTED;
-        goto Exit;
+        /* Use 1 page alignment if none was requested */
+        if (!Alignment)
+        {
+            Alignment = 1;
+        }
+
+        /* Check if we got a range */
+        if (Range)
+        {
+            /* We don't support virtual memory yet @TODO */
+            EfiPrintf(L"not yet implemented in %S\r\n", __FUNCTION__);
+            EfiStall(1000000);
+            Status = STATUS_NOT_IMPLEMENTED;
+            goto Exit;
+        }
+        else
+        {
+            /* Use the entire range that's possible */
+            Request.BaseRange.Minimum = PapMinimumPhysicalPage;
+            Request.BaseRange.Maximum = 0xFFFFFFFF >> PAGE_SHIFT;
+        }
+
+        /* Check if a fixed allocation was requested */
+        if (Attributes & BlMemoryFixed)
+        {
+            /* We don't support virtual memory yet @TODO */
+            EfiPrintf(L"not yet implemented in %S\r\n", __FUNCTION__);
+            EfiStall(1000000);
+            Status = STATUS_NOT_IMPLEMENTED;
+            goto Exit;
+        }
+        else
+        {
+            /* Check if non-fixed was specifically requested */
+            if (Attributes & BlMemoryKernelRange)
+            {
+                /* We don't support virtual memory yet @TODO */
+                EfiPrintf(L"not yet implemented in %S\r\n", __FUNCTION__);
+                EfiStall(1000000);
+                Status = STATUS_NOT_IMPLEMENTED;
+                goto Exit;
+            }
+
+            /* Set the virtual address range */
+            Request.VirtualRange.Minimum = 0;
+            Request.VirtualRange.Maximum = 0xFFFFFFFF >> PAGE_SHIFT;
+        }
+
+        /* Check what type of allocation was requested */
+        if ((Type) && !(Type & ~(BL_MM_REQUEST_DEFAULT_TYPE |
+                               BL_MM_REQUEST_TOP_DOWN_TYPE)))
+        {
+            /* Save it if it was valid */
+            Request.Type = Type;
+        }
+        else
+        {
+            /* Set the default */
+            Request.Type = BL_MM_REQUEST_DEFAULT_TYPE;
+        }
+
+        /* Fill out the request of the request */
+        Request.Flags = Attributes;
+        Request.Alignment = Alignment;
+        Request.Pages = Pages;
+
+        /* Try to allocate the pages */
+        Status = MmPaAllocatePages(List,
+                                   &Descriptor,
+                                   &MmMdlMappedUnallocated,
+                                   &Request,
+                                   MemoryType);
+        if (!NT_SUCCESS(Status))
+        {
+            /* Extend the physical allocator */
+            Status = MmPapPageAllocatorExtend(Attributes,
+                                              Alignment,
+                                              Pages,
+                                              ((ULONG_PTR)*PhysicalAddress) >>
+                                              PAGE_SHIFT,
+                                              Range,
+                                              Type);
+            if (!NT_SUCCESS(Status))
+            {
+                /* Fail since we're out of memory */
+                EfiPrintf(L"OUT OF MEMORY: %lx\r\n", Status);
+                Status = STATUS_NO_MEMORY;
+                goto Exit;
+            }
+
+            /* Try the allocation again now */
+            Status = MmPaAllocatePages(&MmMdlMappedAllocated,
+                                       &Descriptor,
+                                       &MmMdlMappedUnallocated,
+                                       &Request,
+                                       MemoryType);
+            if (!NT_SUCCESS(Status))
+            {
+                /* Fail since we're out of memory */
+                EfiPrintf(L"OUT OF MEMORY: %lx\r\n", Status);
+                goto Exit;
+            }
+        }
+
+        /* Return the allocated address */
+        *PhysicalAddress = (PVOID)((ULONG_PTR)Descriptor.VirtualPage << PAGE_SHIFT);
     }
     else
     {
         /* Check if this is a fixed allocation */
-        BaseAddress.QuadPart = (Attributes & BlMemoryFixed) ? (ULONG_PTR)*PhysicalAddress : 0;
+        BaseAddress.QuadPart = (Attributes & BlMemoryFixed) ?
+                                (ULONG_PTR)*PhysicalAddress : 0;
 
         /* Allocate the pages */
         Status = MmPapAllocatePhysicalPagesInRange(&BaseAddress,
@@ -542,6 +824,7 @@ MmPaInitialize (
         while (ExistingDescriptors != 0)
         {
             /* Remove this region from our free memory MDL */
+            //EfiPrintf(L"Handling existing descriptor: %llx %llx\r\n", Descriptor->BasePage, Descriptor->PageCount);
             Status = MmMdRemoveRegionFromMdlEx(&MmMdlUnmappedUnallocated,
                                                BL_MM_REMOVE_PHYSICAL_REGION_FLAG,
                                                Descriptor->BasePage,
@@ -628,7 +911,7 @@ MmPapFreePhysicalPages (
     DontFree = FALSE;
     HasPageData = FALSE;
 
-    /* Only page-aligned addresses a re accepted */
+    /* Only page-aligned addresses are accepted */
     if (Address.QuadPart & (PAGE_SIZE - 1))
     {
         EfiPrintf(L"free mem fail 1\r\n");
@@ -740,7 +1023,7 @@ MmPapFreePhysicalPages (
         Flags |= BL_MM_ADD_DESCRIPTOR_NEVER_COALESCE_FLAG;
     }
 
-    /* Check if the entire allocation is being free*/
+    /* Check if the entire allocation is being free*/
     if (PageCount == Descriptor->PageCount)
     {
         /* Remove the descriptor from the allocated list */
@@ -1096,3 +1379,202 @@ Quickie:
     MmMdFreeList(&FirmwareMdList);
     return Status;
 }
+
+NTSTATUS
+MmPaReleaseSelfMapPages (
+    _In_ PHYSICAL_ADDRESS Address
+    )
+{
+    PBL_MEMORY_DESCRIPTOR Descriptor;
+    ULONGLONG BasePage;
+    NTSTATUS Status;
+
+    /* Only page-aligned addresses are accepted */
+    if (Address.QuadPart & (PAGE_SIZE - 1))
+    {
+        EfiPrintf(L"free mem fail 1\r\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Get the base page, and find a descriptor that matches */
+    BasePage = Address.QuadPart >> PAGE_SHIFT;
+    Descriptor = MmMdFindDescriptor(BL_MM_INCLUDE_UNMAPPED_UNALLOCATED,
+                                    BL_MM_REMOVE_PHYSICAL_REGION_FLAG,
+                                    BasePage);
+    if (!(Descriptor) || (Descriptor->BasePage != BasePage))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Free the physical pages */
+    Status = MmFwFreePages(BasePage, Descriptor->PageCount);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* Remove the firmware flags */
+    Descriptor->Flags &= ~(BlMemoryNonFirmware |
+                           BlMemoryFirmware |
+                           BlMemoryPersistent);
+
+    /* Set it as free memory */
+    Descriptor->Type = BlConventionalMemory;
+
+    /* Create a new descriptor that's free memory, covering the old range */
+    Descriptor = MmMdInitByteGranularDescriptor(0,
+                                                BlConventionalMemory,
+                                                BasePage,
+                                                0,
+                                                Descriptor->PageCount);
+    if (!Descriptor)
+    {
+        return STATUS_NO_MEMORY;
+    }
+
+    /* Insert it into the virtual free list */
+    return MmMdAddDescriptorToList(&MmMdlFreeVirtual,
+                                   Descriptor,
+                                   BL_MM_ADD_DESCRIPTOR_COALESCE_FLAG |
+                                   BL_MM_ADD_DESCRIPTOR_TRUNCATE_FLAG);
+}
+
+NTSTATUS
+MmPaReserveSelfMapPages (
+    _Inout_ PPHYSICAL_ADDRESS PhysicalAddress,
+    _In_ ULONG Alignment, 
+    _In_ ULONG PageCount
+    )
+{
+    NTSTATUS Status;
+    BL_PA_REQUEST Request;
+    BL_MEMORY_DESCRIPTOR Descriptor;
+
+    /* Increment descriptor usage count */
+    ++MmDescriptorCallTreeCount;
+
+    /* Bail if we don't have an address */
+    if (!PhysicalAddress)
+    {
+        Status = STATUS_INVALID_PARAMETER;
+        goto Quickie;
+    }
+
+    /* Make a request for the required number of self-map pages */
+    Request.BaseRange.Minimum = PapMinimumPhysicalPage;
+    Request.BaseRange.Maximum = 0xFFFFFFFF >> PAGE_SHIFT;
+    Request.VirtualRange.Minimum = 0;
+    Request.VirtualRange.Maximum = 0;
+    Request.Pages = PageCount;
+    Request.Alignment = Alignment;
+    Request.Type = BL_MM_REQUEST_DEFAULT_TYPE;
+    Request.Flags = 0;;
+    Status = MmPaAllocatePages(&MmMdlUnmappedUnallocated,
+                               &Descriptor,
+                               &MmMdlUnmappedUnallocated,
+                               &Request,
+                               BlLoaderSelfMap);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Quickie;
+    }
+
+    /* Remove this region from free virtual memory */
+    Status = MmMdRemoveRegionFromMdlEx(&MmMdlFreeVirtual,
+                                       BL_MM_REMOVE_VIRTUAL_REGION_FLAG,
+                                       Descriptor.BasePage,
+                                       Descriptor.PageCount,
+                                       0);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Quickie;
+    }
+
+    /* Return the physical address */
+    PhysicalAddress->QuadPart = Descriptor.BasePage << PAGE_SHIFT;
+
+Quickie:
+    /* Free global descriptors and reduce the count by one */
+    MmMdFreeGlobalDescriptors();
+    --MmDescriptorCallTreeCount;
+    return Status;
+}
+
+NTSTATUS
+MmSelectMappingAddress (
+    _Out_ PVOID* MappingAddress,
+    _In_ PVOID PreferredAddress, 
+    _In_ ULONGLONG Size,
+    _In_ ULONG AllocationAttributes,
+    _In_ ULONG Flags,
+    _In_ PHYSICAL_ADDRESS PhysicalAddress
+    )
+{
+    BL_PA_REQUEST Request;
+    NTSTATUS Status;
+    BL_MEMORY_DESCRIPTOR NewDescriptor;
+
+    /* Are we in physical mode? */
+    if (MmTranslationType == BlNone)
+    {
+        /* Just return the physical address as the mapping address */
+        PreferredAddress = (PVOID)PhysicalAddress.LowPart;
+    }
+
+    /* If no physical address, or caller wants a fixed address... */
+    if ((PhysicalAddress.QuadPart == -1) || (Flags & BlMemoryFixed))
+    {
+        /* Then just return the preferred address */
+        goto Success;
+    }
+
+    /* Check which range of virtual memory should be used */
+    if (AllocationAttributes & BlMemoryKernelRange)
+    {
+        /* Use kernel range */
+        Request.BaseRange = MmArchKsegAddressRange;
+        Request.Type = BL_MM_REQUEST_DEFAULT_TYPE;
+    }
+    else
+    {
+        /* User user/application range */
+        Request.BaseRange.Minimum = 0;
+        Request.BaseRange.Maximum = MmArchTopOfApplicationAddressSpace;
+        Request.Type = BL_MM_REQUEST_TOP_DOWN_TYPE;
+    }
+
+    /* Build a request */
+    Request.VirtualRange.Minimum = 0;
+    Request.VirtualRange.Maximum = 0;
+    Request.Flags = AllocationAttributes & BlMemoryLargePages;
+    Request.Alignment = 1;
+    Request.Pages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(PhysicalAddress.LowPart, Size);
+
+    /* Allocate the physical pages */
+    Status = MmPaAllocatePages(NULL,
+                               &NewDescriptor,
+                               &MmMdlFreeVirtual,
+                               &Request,
+                               BlConventionalMemory);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* Return the address we got back */
+    PreferredAddress = (PVOID)((ULONG_PTR)NewDescriptor.BasePage << PAGE_SHIFT);
+
+    /* Check if the existing physical address was not aligned */
+    if (PhysicalAddress.QuadPart != -1)
+    {
+        /* Add the offset to the returned virtual address */
+        PreferredAddress = (PVOID)((ULONG_PTR)PreferredAddress +
+                                   PhysicalAddress.LowPart -
+                                   BYTE_OFFSET(PhysicalAddress.LowPart));
+    }
+    
+Success:
+    /* Return the mapping address and success */
+    *MappingAddress = PreferredAddress;
+    return STATUS_SUCCESS;
+}