[FREELDR] Formatting only.
[reactos.git] / boot / freeldr / freeldr / arch / i386 / pcmem.c
index bf04553..3fcab1f 100644 (file)
@@ -1,6 +1,9 @@
 /*
  *  FreeLoader
  *
+ * Copyright ... ... (See below.)
+ * Copyright 2017 Serge Gautherie <reactos-git_serge_171003@gautherie.fr>
+ *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
 #include <freeldr.h>
 #include <arch/pc/x86common.h>
 
-#define NDEBUG
 #include <debug.h>
-
 DBG_DEFAULT_CHANNEL(MEMORY);
 
-#define MAX_BIOS_DESCRIPTORS 32
-#define FREELDR_BASE_PAGE  (FREELDR_BASE / PAGE_SIZE)
-#define DISKBUF_BASE_PAGE  (DISKREADBUFFER / PAGE_SIZE)
-#define STACK_BASE_PAGE    (STACKLOWLIMIT / PAGE_SIZE)
-#define STACK_END_PAGE     (STACK32ADDR / PAGE_SIZE)
-#define BIOSBUF_BASE_PAGE  (BIOSCALLBUFFER / PAGE_SIZE)
+#define ULONGLONG_ALIGN_DOWN_BY(size, align) \
+    ((ULONGLONG)(size) & ~((ULONGLONG)(align) - 1))
 
-#define FREELDR_PAGE_COUNT (DISKBUF_BASE_PAGE - FREELDR_BASE_PAGE)
-#define DISKBUF_PAGE_COUNT (STACK_BASE_PAGE - DISKBUF_BASE_PAGE)
-#define STACK_PAGE_COUNT   (STACK_END_PAGE - STACK_BASE_PAGE)
-#define BIOSBUF_PAGE_COUNT (1)
+#define ULONGLONG_ALIGN_UP_BY(size, align) \
+    (ULONGLONG_ALIGN_DOWN_BY(((ULONGLONG)(size) + align - 1), align))
 
 BIOS_MEMORY_MAP PcBiosMemoryMap[MAX_BIOS_DESCRIPTORS];
 ULONG PcBiosMapCount;
 
-FREELDR_MEMORY_DESCRIPTOR PcMemoryMap[MAX_BIOS_DESCRIPTORS + 1] =
-{
- { LoaderFirmwarePermanent, 0x00,               1 }, // realmode int vectors
- { LoaderFirmwareTemporary, 0x01,               FREELDR_BASE_PAGE - 1 }, // freeldr stack + cmdline
- { LoaderLoadedProgram,     FREELDR_BASE_PAGE,  FREELDR_PAGE_COUNT }, // freeldr image
- { LoaderFirmwareTemporary, DISKBUF_BASE_PAGE,  DISKBUF_PAGE_COUNT }, // Disk read buffer for int 13h. DISKREADBUFFER
- { LoaderOsloaderStack,     STACK_BASE_PAGE,    STACK_PAGE_COUNT }, // prot mode stack.
- { LoaderFirmwareTemporary, BIOSBUF_BASE_PAGE,  BIOSBUF_PAGE_COUNT }, // BIOSCALLBUFFER
- { LoaderFirmwarePermanent, 0xA0,               0x50 }, // ROM / Video
- { LoaderSpecialMemory,     0xF0,               0x10 }, // ROM / Video
- { LoaderSpecialMemory,     0xFFF,              1 }, // unusable memory
- { 0, 0, 0 }, // end of map
-};
+FREELDR_MEMORY_DESCRIPTOR PcMemoryMap[MAX_BIOS_DESCRIPTORS + 1];
+ULONG PcMapCount;
 
 ULONG
 AddMemoryDescriptor(
@@ -95,7 +79,7 @@ GetExtendedMemoryConfiguration(ULONG* pMemoryAtOneMB /* in KB */, ULONG* pMemory
     TRACE("BX = 0x%x\n", RegsOut.w.bx);
     TRACE("CX = 0x%x\n", RegsOut.w.cx);
     TRACE("DX = 0x%x\n", RegsOut.w.dx);
-    TRACE("CF set = %s\n\n", (RegsOut.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
+    TRACE("CF set = %s\n", (RegsOut.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
 
     if (INT386_SUCCESS(RegsOut))
     {
@@ -134,7 +118,7 @@ GetExtendedMemoryConfiguration(ULONG* pMemoryAtOneMB /* in KB */, ULONG* pMemory
 
     TRACE("Int15h AH=88h\n");
     TRACE("AX = 0x%x\n", RegsOut.w.ax);
-    TRACE("CF set = %s\n\n", (RegsOut.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
+    TRACE("CF set = %s\n", (RegsOut.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
 
     if (INT386_SUCCESS(RegsOut) && RegsOut.w.ax != 0)
     {
@@ -150,7 +134,7 @@ GetExtendedMemoryConfiguration(ULONG* pMemoryAtOneMB /* in KB */, ULONG* pMemory
     *pMemoryAtOneMB = (*pMemoryAtOneMB << 8);
 
     TRACE("Int15h Failed\n");
-    TRACE("CMOS reports: 0x%x\n", *pMemoryAtOneMB);
+    TRACE("CMOS reports: 0x%lx\n", *pMemoryAtOneMB);
 
     if (*pMemoryAtOneMB != 0)
     {
@@ -160,204 +144,517 @@ GetExtendedMemoryConfiguration(ULONG* pMemoryAtOneMB /* in KB */, ULONG* pMemory
     return FALSE;
 }
 
-static ULONG
+static
+ULONG
 PcMemGetConventionalMemorySize(VOID)
 {
-  REGS Regs;
-
-  TRACE("GetConventionalMemorySize()\n");
-
-  /* Int 12h
-   * BIOS - GET MEMORY SIZE
-   *
-   * Return:
-   * AX = kilobytes of contiguous memory starting at absolute address 00000h
-   *
-   * This call returns the contents of the word at 0040h:0013h;
-   * in PC and XT, this value is set from the switches on the motherboard
-   */
-  Regs.w.ax = 0;
-  Int386(0x12, &Regs, &Regs);
-
-  TRACE("Int12h\n");
-  TRACE("AX = 0x%x\n\n", Regs.w.ax);
-
-  return (ULONG)Regs.w.ax;
+    REGS Regs;
+
+    TRACE("PcMemGetConventionalMemorySize()\n");
+
+    /* Int 12h
+     * BIOS - GET MEMORY SIZE
+     *
+     * Return:
+     * AX = kilobytes of contiguous memory starting at absolute address 00000h
+     *
+     * This call returns the contents of the word at 0040h:0013h;
+     * in PC and XT, this value is set from the switches on the motherboard
+     */
+    Regs.w.ax = 0;
+    Int386(0x12, &Regs, &Regs);
+
+    TRACE("Int12h\n");
+    TRACE("AX = 0x%x\n", Regs.w.ax);
+
+    return (ULONG)Regs.w.ax;
+}
+
+static
+BOOLEAN
+GetEbdaLocation(
+    PULONG BaseAddress,
+    PULONG Size)
+{
+    REGS Regs;
+
+    TRACE("GetEbdaLocation()\n");
+
+    /* Get the address of the Extended BIOS Data Area (EBDA).
+     * Int 15h, AH=C1h
+     * SYSTEM - RETURN EXTENDED-BIOS DATA-AREA SEGMENT ADDRESS (PS)
+     *
+     * Return:
+     * CF set on error
+     * CF clear if successful
+     * ES = segment of data area
+     */
+    Regs.x.eax = 0x0000C100;
+    Int386(0x15, &Regs, &Regs);
+
+    /* If the function fails, there is no EBDA */
+    if (!INT386_SUCCESS(Regs))
+    {
+        return FALSE;
+    }
+
+    /* Get Base address and (maximum) size */
+    *BaseAddress = (ULONG)Regs.w.es << 4;
+    *Size = 0xA0000 - *BaseAddress;
+    return TRUE;
+}
+
+static
+VOID
+PcMemCheckUsableMemorySize(VOID)
+{
+    ULONG Size, RequiredSize;
+
+    TRACE("PcMemCheckUsableMemorySize()\n");
+
+    /* Make sure the usable memory is large enough. To do this we check the 16
+       bit value at address 0x413 inside the BDA, which gives us the usable size
+       in KB */
+    Size = (*(PUSHORT)(ULONG_PTR)0x413) * 1024;
+    RequiredSize = FREELDR_BASE + FrLdrImageSize + PAGE_SIZE;
+    if (Size < RequiredSize)
+    {
+        FrLdrBugCheckWithMessage(
+            MEMORY_INIT_FAILURE,
+            __FILE__,
+            __LINE__,
+            "The BIOS reported a usable memory range up to 0x%lx, which is too small!\n"
+            "Required size is 0x%lx\n\n"
+            "If you see this, please report to the ReactOS team!",
+            Size, RequiredSize);
+    }
 }
 
 static
 ULONG
 PcMemGetBiosMemoryMap(PFREELDR_MEMORY_DESCRIPTOR MemoryMap, ULONG MaxMemoryMapSize)
 {
-  REGS Regs;
-  ULONG MapCount = 0;
-  ULONGLONG RealBaseAddress, RealSize;
-  TYPE_OF_MEMORY MemoryType;
-  ASSERT(PcBiosMapCount == 0);
-
-  TRACE("GetBiosMemoryMap()\n");
-
-  /* Int 15h AX=E820h
-   * Newer BIOSes - GET SYSTEM MEMORY MAP
-   *
-   * AX = E820h
-   * EAX = 0000E820h
-   * EDX = 534D4150h ('SMAP')
-   * EBX = continuation value or 00000000h to start at beginning of map
-   * ECX = size of buffer for result, in bytes (should be >= 20 bytes)
-   * ES:DI -> buffer for result
-   * Return:
-   * CF clear if successful
-   * EAX = 534D4150h ('SMAP')
-   * ES:DI buffer filled
-   * EBX = next offset from which to copy or 00000000h if all done
-   * ECX = actual length returned in bytes
-   * CF set on error
-   * AH = error code (86h)
-   */
-  Regs.x.ebx = 0x00000000;
-
-  while (PcBiosMapCount < MAX_BIOS_DESCRIPTORS)
+    REGS Regs;
+    ULONGLONG RealBaseAddress, EndAddress, RealSize;
+    TYPE_OF_MEMORY MemoryType;
+
+    ASSERT(PcBiosMapCount == 0);
+
+    TRACE("PcMemGetBiosMemoryMap()\n");
+
+    /* Int 15h AX=E820h
+     * Newer BIOSes - GET SYSTEM MEMORY MAP
+     *
+     * AX = E820h
+     * EAX = 0000E820h
+     * EDX = 534D4150h ('SMAP')
+     * EBX = continuation value or 00000000h to start at beginning of map
+     * ECX = size of buffer for result, in bytes (should be >= 20 bytes)
+     * ES:DI -> buffer for result
+     * Return:
+     * CF clear if successful
+     * EAX = 534D4150h ('SMAP')
+     * ES:DI buffer filled
+     * EBX = next offset from which to copy or 00000000h if all done
+     * ECX = actual length returned in bytes
+     * CF set on error
+     * AH = error code (86h)
+     */
+    Regs.x.ebx = 0x00000000;
+
+    while (PcBiosMapCount < MAX_BIOS_DESCRIPTORS)
     {
-      /* Setup the registers for the BIOS call */
-      Regs.x.eax = 0x0000E820;
-      Regs.x.edx = 0x534D4150; /* ('SMAP') */
-      /* Regs.x.ebx = 0x00000001;  Continuation value already set */
-      Regs.x.ecx = sizeof(BIOS_MEMORY_MAP);
-      Regs.w.es = BIOSCALLBUFSEGMENT;
-      Regs.w.di = BIOSCALLBUFOFFSET;
-      Int386(0x15, &Regs, &Regs);
-
-      TRACE("Memory Map Entry %d\n", PcBiosMapCount);
-      TRACE("Int15h AX=E820h\n");
-      TRACE("EAX = 0x%x\n", Regs.x.eax);
-      TRACE("EBX = 0x%x\n", Regs.x.ebx);
-      TRACE("ECX = 0x%x\n", Regs.x.ecx);
-      TRACE("CF set = %s\n", (Regs.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
-
-      /* If the BIOS didn't return 'SMAP' in EAX then
-       * it doesn't support this call. If CF is set, we're done */
-      if (Regs.x.eax != 0x534D4150 || !INT386_SUCCESS(Regs))
+        /* ACPI 3.0/4.0: Set Extended Attributes to enabled/valid by default, in case entry has no E.A.. */
+        ((PBIOS_MEMORY_MAP)BIOSCALLBUFFER)->ExtendedAttributesAsULONG = 0;
+        ((PBIOS_MEMORY_MAP)BIOSCALLBUFFER)->ExtendedAttributes.Enabled_Reserved = 1;
+
+        /* Setup the registers for the BIOS call */
+        Regs.x.eax = 0x0000E820;
+        Regs.x.edx = 0x534D4150; /* ('SMAP') */
+        /* Regs.x.ebx = 0x00000001;  Continuation value already set */
+        Regs.x.ecx = sizeof(BIOS_MEMORY_MAP);
+        Regs.w.es = BIOSCALLBUFSEGMENT;
+        Regs.w.di = BIOSCALLBUFOFFSET;
+        Int386(0x15, &Regs, &Regs);
+
+        TRACE("Memory Map Entry %lu\n", PcBiosMapCount);
+        TRACE("Int15h AX=E820h\n");
+        TRACE("EAX = 0x%lx\n", Regs.x.eax);
+        TRACE("EBX = 0x%lx\n", Regs.x.ebx);
+        TRACE("ECX = %lu\n", Regs.x.ecx);
+        TRACE("CF set = %s\n", (Regs.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
+
+        /* If the BIOS didn't return 'SMAP' in EAX then
+         * it doesn't support this call. */
+        if (Regs.x.eax != 0x534D4150)
+        {
+            WARN("BIOS doesn't support Int15h AX=E820h!\n");
+            break;
+        }
+
+        /* If the carry flag is set,
+         * then this call was past the last entry, so we're done. */
+        if (!INT386_SUCCESS(Regs))
         {
-          break;
+            TRACE("End of System Memory Map! (Past last)\n");
+            break;
         }
 
-      /* Copy data to global buffer */
-      RtlCopyMemory(&PcBiosMemoryMap[PcBiosMapCount], (PVOID)BIOSCALLBUFFER, Regs.x.ecx);
-
-      TRACE("BaseAddress: 0x%llx\n", PcBiosMemoryMap[PcBiosMapCount].BaseAddress);
-      TRACE("Length: 0x%llx\n", PcBiosMemoryMap[PcBiosMapCount].Length);
-      TRACE("Type: 0x%lx\n", PcBiosMemoryMap[PcBiosMapCount].Type);
-      TRACE("Reserved: 0x%lx\n", PcBiosMemoryMap[PcBiosMapCount].Reserved);
-      TRACE("\n");
-
-      /* Check if this is free memory */
-      if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryUsable)
-      {
-          MemoryType = LoaderFree;
-
-          /* Align up base of memory area */
-          RealBaseAddress = PcBiosMemoryMap[PcBiosMapCount].BaseAddress & ~(MM_PAGE_SIZE - 1ULL);
-
-          /* Calculate the length after aligning the base */
-          RealSize = PcBiosMemoryMap[PcBiosMapCount].BaseAddress +
-                     PcBiosMemoryMap[PcBiosMapCount].Length - RealBaseAddress;
-          RealSize = (RealSize + MM_PAGE_SIZE - 1) & ~(MM_PAGE_SIZE - 1ULL);
-      }
-      else
-      {
-          if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryReserved)
-             MemoryType = LoaderFirmwarePermanent;
-          else
-             MemoryType = LoaderSpecialMemory;
-
-          /* Align down base of memory area */
-          RealBaseAddress = PcBiosMemoryMap[PcBiosMapCount].BaseAddress & ~(MM_PAGE_SIZE - 1ULL);
-          /* Calculate the length after aligning the base */
-          RealSize = PcBiosMemoryMap[PcBiosMapCount].BaseAddress +
-                     PcBiosMemoryMap[PcBiosMapCount].Length - RealBaseAddress;
-          RealSize = (RealSize + MM_PAGE_SIZE - 1) & ~(MM_PAGE_SIZE - 1ULL);
-      }
-
-      /* Check if we can add this descriptor */
-      if ((RealSize >= MM_PAGE_SIZE) && (MapCount < MaxMemoryMapSize))
-      {
-        /* Add the descriptor */
-        MapCount = AddMemoryDescriptor(PcMemoryMap,
-                                       MAX_BIOS_DESCRIPTORS,
-                                       (PFN_NUMBER)(RealBaseAddress / MM_PAGE_SIZE),
-                                       (PFN_NUMBER)(RealSize / MM_PAGE_SIZE),
-                                       MemoryType);
-      }
-
-      PcBiosMapCount++;
-
-      /* If the continuation value is zero or the
-       * carry flag is set then this was
-       * the last entry so we're done */
-      if (Regs.x.ebx == 0x00000000)
+        if (Regs.x.ecx == 0)
         {
-          TRACE("End Of System Memory Map!\n\n");
-          break;
+            TRACE("Discard empty entry. (would-be-PcBiosMapCount = %lu)\n",
+                  PcBiosMapCount);
+            goto nextRange;
         }
 
+        /* Extra safety: unexpected entry length.
+         * All in-between values are valid too, as x86 is little-indian
+         * and only lower byte is used per ACPI 6.2-A.
+         */
+        if (Regs.x.ecx < RTL_SIZEOF_THROUGH_FIELD(BIOS_MEMORY_MAP, Type) ||
+            Regs.x.ecx > sizeof(BIOS_MEMORY_MAP))
+        {
+            ERR("Int 15h AX=E820h returned an invalid entry length! (would-be-PcBiosMapCount = %lu, Entry length = (%Iu <=) %lu (<= %Iu))\n",
+                PcBiosMapCount, RTL_SIZEOF_THROUGH_FIELD(BIOS_MEMORY_MAP, Type), Regs.x.ecx, sizeof(BIOS_MEMORY_MAP));
+            /* Warn user, unless wrong case is "first and not too big entry", which is otherwise harmless. */
+            if (PcBiosMapCount > 0 || Regs.x.ecx > sizeof(BIOS_MEMORY_MAP))
+            {
+                ASSERTMSG("Int 15h AX=E820h returned an invalid entry length!\n", FALSE);
+            }
+            /* We keep previous entries (if any), but do not dare trying next entries.
+             * We assume these entries are good to use as is. If they are not, we are in trouble...
+             * (And don't ask what happens if BIOS actually overflowed our entry buffer...)
+             *
+             * FIXME: Safer = revert previous entries, Safest = blacklist this BIOS.
+             */
+            break;
+        }
+
+        if (((PBIOS_MEMORY_MAP)BIOSCALLBUFFER)->ExtendedAttributes.Enabled_Reserved == 0)
+        {
+            WARN("Discard disabled/invalid entry. (would-be-PcBiosMapCount = %lu)\n",
+                 PcBiosMapCount);
+            /* This unlikely case was correct between ACPI 3.0 and 4.0, so assume all is fine.
+             * Unless we would be ready to drop ACPI 3.0 compatibility.
+             */
+            goto nextRange;
+        }
+
+        /* Copy data to global buffer */
+        RtlCopyMemory(&PcBiosMemoryMap[PcBiosMapCount], (PVOID)BIOSCALLBUFFER, sizeof(BIOS_MEMORY_MAP));
+
+        TRACE("BaseAddress: 0x%llx\n", PcBiosMemoryMap[PcBiosMapCount].BaseAddress);
+        TRACE("Length: 0x%llx\n", PcBiosMemoryMap[PcBiosMapCount].Length);
+        TRACE("Type: 0x%lx\n", PcBiosMemoryMap[PcBiosMapCount].Type);
+        TRACE("ExtendedAttributesAsULONG: 0x%08lx\n", PcBiosMemoryMap[PcBiosMapCount].ExtendedAttributesAsULONG);
+
+        if (PcBiosMemoryMap[PcBiosMapCount].ExtendedAttributes.ErrorLog == 1)
+        {
+            FIXME("EA.ErrorLog = 1. Please report this to CORE-14150. "
+                   "(PcBiosMapCount = %lu, BaseAddress = 0x%llx, Length = 0x%llx, Type = 0x%lx, ExtendedAttributesAsULONG = 0x%08lx)\n",
+                  PcBiosMapCount,
+                  PcBiosMemoryMap[PcBiosMapCount].BaseAddress,
+                  PcBiosMemoryMap[PcBiosMapCount].Length,
+                  PcBiosMemoryMap[PcBiosMapCount].Type,
+                  PcBiosMemoryMap[PcBiosMapCount].ExtendedAttributesAsULONG);
+            // NotWantedForPublicBuilds: ASSERTMSG("EA.ErrorLog = 1. Check/Report then CONTinue.\n", FALSE);
+        }
+
+        if (PcBiosMemoryMap[PcBiosMapCount].Length == 0)
+        {
+            TRACE("Discard empty range. (would-be-PcBiosMapCount = %lu, BaseAddress = 0x%llx, Length = 0)\n",
+                  PcBiosMapCount, PcBiosMemoryMap[PcBiosMapCount].BaseAddress);
+            goto nextRange;
+        }
+
+        /* Check if this is free memory */
+        if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryUsable)
+        {
+            MemoryType = LoaderFree;
+
+            /* Align up base of memory range */
+            RealBaseAddress = ULONGLONG_ALIGN_UP_BY(
+                                PcBiosMemoryMap[PcBiosMapCount].BaseAddress,
+                                PAGE_SIZE);
+
+            /* Calculate aligned EndAddress */
+            EndAddress = PcBiosMemoryMap[PcBiosMapCount].BaseAddress +
+                         PcBiosMemoryMap[PcBiosMapCount].Length;
+            EndAddress = ULONGLONG_ALIGN_DOWN_BY(EndAddress, PAGE_SIZE);
+
+            /* Check if there is anything left */
+            if (EndAddress <= RealBaseAddress)
+            {
+                /* This doesn't span any page, so continue with next range */
+                TRACE("Skipping aligned range < PAGE_SIZE. (would-be-PcBiosMapCount = %lu, BaseAddress = 0x%llx, Length = 0x%llx)\n",
+                      PcBiosMapCount,
+                      PcBiosMemoryMap[PcBiosMapCount].BaseAddress,
+                      PcBiosMemoryMap[PcBiosMapCount].Length);
+                goto nextRange;
+            }
+
+            /* Calculate the length of the aligned range */
+            RealSize = EndAddress - RealBaseAddress;
+        }
+        else
+        {
+            if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryReserved)
+            {
+                MemoryType = LoaderFirmwarePermanent;
+            }
+            else
+            {
+                MemoryType = LoaderSpecialMemory;
+            }
+
+            /* Align down base of memory area */
+            RealBaseAddress = ULONGLONG_ALIGN_DOWN_BY(
+                                PcBiosMemoryMap[PcBiosMapCount].BaseAddress,
+                                PAGE_SIZE);
+
+            /* Calculate the length after aligning the base */
+            RealSize = PcBiosMemoryMap[PcBiosMapCount].BaseAddress +
+                       PcBiosMemoryMap[PcBiosMapCount].Length - RealBaseAddress;
+            RealSize = ULONGLONG_ALIGN_UP_BY(RealSize, PAGE_SIZE);
+        }
+
+        /* Check if we can add this descriptor */
+        if (RealSize < MM_PAGE_SIZE)
+        {
+            TRACE("Skipping aligned range < MM_PAGE_SIZE. (PcBiosMapCount = %lu, BaseAddress = 0x%llx, Length = 0x%llx)\n",
+                  PcBiosMapCount,
+                  PcBiosMemoryMap[PcBiosMapCount].BaseAddress,
+                  PcBiosMemoryMap[PcBiosMapCount].Length);
+        }
+        else if (PcMapCount >= MaxMemoryMapSize)
+        {
+            ERR("PcMemoryMap is already full! (PcBiosMapCount = %lu, PcMapCount = %lu (>= %lu))\n",
+                PcBiosMapCount, PcMapCount, MaxMemoryMapSize);
+            // NotWantedForPublicBuilds: ASSERTMSG("PcMemoryMap is already full!\n", FALSE);
+            /* We keep previous entries, and half-retrieve current/next entries.
+             * We assume all these entries are good to use as is. If they are not, we are in trouble...
+             *
+             * FIXME: Safer = revert (half-)retrieved entries, Safest = increase MaxMemoryMapSize.
+             */
+        }
+        else
+        {
+            /* Add the descriptor */
+            PcMapCount = AddMemoryDescriptor(PcMemoryMap,
+                                           MAX_BIOS_DESCRIPTORS,
+                                           (PFN_NUMBER)(RealBaseAddress / MM_PAGE_SIZE),
+                                           (PFN_NUMBER)(RealSize / MM_PAGE_SIZE),
+                                           MemoryType);
+        }
+
+        PcBiosMapCount++;
+
+nextRange:
+        /* If the continuation value is zero,
+         * then this was the last entry, so we're done. */
+        if (Regs.x.ebx == 0x00000000)
+        {
+            TRACE("End of System Memory Map! (Reset)\n");
+            break;
+        }
+    }
+    /* Check whether there would be more entries to process. */
+    if (PcBiosMapCount >= MAX_BIOS_DESCRIPTORS && Regs.x.ebx != 0x00000000)
+    {
+        ERR("PcBiosMemoryMap is already full! (PcBiosMapCount = %lu (>= %lu), PcMapCount = %lu)\n",
+            PcBiosMapCount, MAX_BIOS_DESCRIPTORS, PcMapCount);
+        // NotWantedForPublicBuilds: ASSERTMSG("PcBiosMemoryMap is already full!\n", FALSE);
+        /* We keep retrieved entries, but ignore next entries.
+         * We assume these entries are good to use as is. If they are not, we are in trouble...
+         *
+         * FIXME: Safer = revert retrieved entries, Safest = increase MAX_BIOS_DESCRIPTORS.
+         */
     }
 
-  return MapCount;
+    TRACE("PcMemGetBiosMemoryMap end: PcBiosMapCount = %lu\n", PcBiosMapCount);
+    return PcBiosMapCount;
 }
 
+VOID
+ReserveMemory(
+    PFREELDR_MEMORY_DESCRIPTOR MemoryMap,
+    ULONG_PTR BaseAddress,
+    SIZE_T Size,
+    TYPE_OF_MEMORY MemoryType,
+    PCHAR Usage)
+{
+    ULONG_PTR BasePage, PageCount;
+    ULONG i;
+
+    BasePage = BaseAddress / PAGE_SIZE;
+    PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(BaseAddress, Size);
 
-PFREELDR_MEMORY_DESCRIPTOR
-PcMemGetMemoryMap(ULONG *MemoryMapSize)
+    for (i = 0; i < PcMapCount; i++)
+    {
+        /* Check for conflicting descriptor */
+        if ((MemoryMap[i].BasePage < BasePage + PageCount) &&
+            (MemoryMap[i].BasePage + MemoryMap[i].PageCount > BasePage))
+        {
+            /* Check if the memory is free */
+            if (MemoryMap[i].MemoryType != LoaderFree)
+            {
+                FrLdrBugCheckWithMessage(
+                    MEMORY_INIT_FAILURE,
+                    __FILE__,
+                    __LINE__,
+                    "Failed to reserve memory in the range 0x%Ix - 0x%Ix for %s",
+                    BaseAddress,
+                    Size,
+                    Usage);
+            }
+        }
+    }
+
+    /* Add the memory descriptor */
+    PcMapCount = AddMemoryDescriptor(MemoryMap,
+                                     MAX_BIOS_DESCRIPTORS,
+                                     BasePage,
+                                     PageCount,
+                                     MemoryType);
+}
+
+VOID
+SetMemory(
+    PFREELDR_MEMORY_DESCRIPTOR MemoryMap,
+    ULONG_PTR BaseAddress,
+    SIZE_T Size,
+    TYPE_OF_MEMORY MemoryType)
+{
+    ULONG_PTR BasePage, PageCount;
+
+    BasePage = BaseAddress / PAGE_SIZE;
+    PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(BaseAddress, Size);
+
+    /* Add the memory descriptor */
+    PcMapCount = AddMemoryDescriptor(MemoryMap,
+                                     MAX_BIOS_DESCRIPTORS,
+                                     BasePage,
+                                     PageCount,
+                                     MemoryType);
+}
+
+ULONG
+PcMemFinalizeMemoryMap(
+    PFREELDR_MEMORY_DESCRIPTOR MemoryMap)
 {
-  ULONG i, EntryCount;
-  ULONG ExtendedMemorySizeAtOneMB;
-  ULONG ExtendedMemorySizeAtSixteenMB;
+    ULONG i;
+
+    /* Reserve some static ranges for freeldr */
+    ReserveMemory(MemoryMap, 0x1000, STACKLOW - 0x1000, LoaderFirmwareTemporary, "BIOS area");
+    ReserveMemory(MemoryMap, STACKLOW, STACKADDR - STACKLOW, LoaderOsloaderStack, "FreeLdr stack");
+    ReserveMemory(MemoryMap, FREELDR_BASE, FrLdrImageSize, LoaderLoadedProgram, "FreeLdr image");
 
-  EntryCount = PcMemGetBiosMemoryMap(PcMemoryMap, MAX_BIOS_DESCRIPTORS);
+    /* Default to 1 page above freeldr for the disk read buffer */
+    DiskReadBuffer = (PUCHAR)ALIGN_UP_BY(FREELDR_BASE + FrLdrImageSize, PAGE_SIZE);
+    DiskReadBufferSize = PAGE_SIZE;
 
-  /* If the BIOS didn't provide a memory map, synthesize one */
-  if (0 == EntryCount)
+    /* Scan for free range above freeldr image */
+    for (i = 0; i < PcMapCount; i++)
     {
-      GetExtendedMemoryConfiguration(&ExtendedMemorySizeAtOneMB, &ExtendedMemorySizeAtSixteenMB);
-
-      /* Conventional memory */
-      AddMemoryDescriptor(PcMemoryMap,
-                          MAX_BIOS_DESCRIPTORS,
-                          0,
-                          PcMemGetConventionalMemorySize() * 1024 / PAGE_SIZE,
-                          LoaderFree);
-
-      /* Extended memory */
-      EntryCount = AddMemoryDescriptor(PcMemoryMap,
-                          MAX_BIOS_DESCRIPTORS,
-                          1024 * 1024 / PAGE_SIZE,
-                          ExtendedMemorySizeAtOneMB * 1024 / PAGE_SIZE,
-                          LoaderFree);
-
-      if (ExtendedMemorySizeAtSixteenMB != 0)
-      {
-        /* Extended memory at 16MB */
-        EntryCount = AddMemoryDescriptor(PcMemoryMap,
-                          MAX_BIOS_DESCRIPTORS,
-                          0x1000000 / PAGE_SIZE,
-                          ExtendedMemorySizeAtSixteenMB * 64 * 1024 / PAGE_SIZE,
-                          LoaderFree);
-      }
+        if ((MemoryMap[i].BasePage > (FREELDR_BASE / PAGE_SIZE)) &&
+            (MemoryMap[i].MemoryType == LoaderFree))
+        {
+            /* Use this range for the disk read buffer */
+            DiskReadBuffer = (PVOID)(MemoryMap[i].BasePage * PAGE_SIZE);
+            DiskReadBufferSize = min(MemoryMap[i].PageCount * PAGE_SIZE,
+                                     MAX_DISKREADBUFFER_SIZE);
+            break;
+        }
     }
 
+    TRACE("DiskReadBuffer=0x%p, DiskReadBufferSize=0x%lx\n",
+          DiskReadBuffer, DiskReadBufferSize);
+
+    ASSERT(DiskReadBufferSize > 0);
+
+    /* Now reserve the range for the disk read buffer */
+    ReserveMemory(MemoryMap,
+                  (ULONG_PTR)DiskReadBuffer,
+                  DiskReadBufferSize,
+                  LoaderFirmwareTemporary,
+                  "Disk read buffer");
+
     TRACE("Dumping resulting memory map:\n");
-    for (i = 0; i < EntryCount; i++)
+    for (i = 0; i < PcMapCount; i++)
     {
         TRACE("BasePage=0x%lx, PageCount=0x%lx, Type=%s\n",
-              PcMemoryMap[i].BasePage,
-              PcMemoryMap[i].PageCount,
-              MmGetSystemMemoryMapTypeString(PcMemoryMap[i].MemoryType));
+              MemoryMap[i].BasePage,
+              MemoryMap[i].PageCount,
+              MmGetSystemMemoryMapTypeString(MemoryMap[i].MemoryType));
+    }
+    return PcMapCount;
+}
+
+PFREELDR_MEMORY_DESCRIPTOR
+PcMemGetMemoryMap(ULONG *MemoryMapSize)
+{
+    ULONG EntryCount;
+    ULONG ExtendedMemorySizeAtOneMB;
+    ULONG ExtendedMemorySizeAtSixteenMB;
+    ULONG EbdaBase, EbdaSize;
+
+    TRACE("PcMemGetMemoryMap()\n");
+
+    PcMemCheckUsableMemorySize();
+
+    EntryCount = PcMemGetBiosMemoryMap(PcMemoryMap, MAX_BIOS_DESCRIPTORS);
+
+    /* If the BIOS didn't provide a memory map, synthesize one */
+    if (EntryCount == 0)
+    {
+        GetExtendedMemoryConfiguration(&ExtendedMemorySizeAtOneMB,
+                                       &ExtendedMemorySizeAtSixteenMB);
+
+        /* Conventional memory */
+        AddMemoryDescriptor(PcMemoryMap,
+                            MAX_BIOS_DESCRIPTORS,
+                            0,
+                            PcMemGetConventionalMemorySize() * 1024 / PAGE_SIZE,
+                            LoaderFree);
+
+        /* Extended memory */
+        PcMapCount = AddMemoryDescriptor(PcMemoryMap,
+                                         MAX_BIOS_DESCRIPTORS,
+                                         1024 * 1024 / PAGE_SIZE,
+                                         ExtendedMemorySizeAtOneMB * 1024 / PAGE_SIZE,
+                                         LoaderFree);
+
+        if (ExtendedMemorySizeAtSixteenMB != 0)
+        {
+            /* Extended memory at 16MB */
+            PcMapCount = AddMemoryDescriptor(PcMemoryMap,
+                                             MAX_BIOS_DESCRIPTORS,
+                                             0x1000000 / PAGE_SIZE,
+                                             ExtendedMemorySizeAtSixteenMB * 64 * 1024 / PAGE_SIZE,
+                                             LoaderFree);
+        }
+
+        /* Check if we have an EBDA and get it's location */
+        if (GetEbdaLocation(&EbdaBase, &EbdaSize))
+        {
+            /* Add the descriptor */
+            PcMapCount = AddMemoryDescriptor(PcMemoryMap,
+                                             MAX_BIOS_DESCRIPTORS,
+                                             (EbdaBase / PAGE_SIZE),
+                                             ADDRESS_AND_SIZE_TO_SPAN_PAGES(EbdaBase, EbdaSize),
+                                             LoaderFirmwarePermanent);
+        }
     }
 
-  *MemoryMapSize = EntryCount;
+    /* Setup some protected ranges */
+    SetMemory(PcMemoryMap, 0x000000, 0x01000, LoaderFirmwarePermanent); // Realmode IVT / BDA
+    SetMemory(PcMemoryMap, 0x0A0000, 0x50000, LoaderFirmwarePermanent); // Video memory
+    SetMemory(PcMemoryMap, 0x0F0000, 0x10000, LoaderSpecialMemory); // ROM
+    SetMemory(PcMemoryMap, 0xFFF000, 0x01000, LoaderSpecialMemory); // unusable memory (do we really need this?)
 
-  return PcMemoryMap;
+    *MemoryMapSize = PcMemFinalizeMemoryMap(PcMemoryMap);
+    return PcMemoryMap;
 }
 
+
 /* EOF */