https://git.reactos.org/?p=reactos.git;a=commitdiff;h=96692636e461b5cc4d0f5…
commit 96692636e461b5cc4d0f5e9fe4e937eb3fcb8d84
Author:     Stanislav Motylkov <x86corez(a)gmail.com>
AuthorDate: Tue Dec 31 18:10:34 2019 +0300
Commit:     Hermès BÉLUSCA - MAÏTO <hermes.belusca-maito(a)reactos.org>
CommitDate: Tue Dec 31 16:10:34 2019 +0100
    [FREELDR] Obtain Xbox memory map via multiboot spec (#1971)
    - Also obtain framebuffer memory size the same way.
    References:
    
https://wiki.osdev.org/Detecting_Memory_(x86)#Memory_Map_Via_GRUB
https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-info…
    CORE-16216 CORE-16300
---
 boot/freeldr/freeldr/arch/i386/multiboot.S |  43 ++++++++++++
 boot/freeldr/freeldr/arch/i386/xboxmem.c   | 106 +++++++++++++++++++++++++----
 boot/freeldr/freeldr/arch/i386/xboxvideo.c |  50 +++++++++++++-
 boot/freeldr/freeldr/include/multiboot.h   |  21 ++++++
 4 files changed, 204 insertions(+), 16 deletions(-)
diff --git a/boot/freeldr/freeldr/arch/i386/multiboot.S
b/boot/freeldr/freeldr/arch/i386/multiboot.S
index 6094000a0e0..296ce0de73f 100644
--- a/boot/freeldr/freeldr/arch/i386/multiboot.S
+++ b/boot/freeldr/freeldr/arch/i386/multiboot.S
@@ -29,9 +29,13 @@
  * the header signature and uses the header to load it.
  */
+#define MB_INFO_SIZE                60   /* sizeof(multiboot_info_t) */
 #define MB_INFO_FLAGS_OFFSET        0
 #define MB_INFO_BOOT_DEVICE_OFFSET  12
 #define MB_INFO_COMMAND_LINE_OFFSET 16
+#define MB_INFO_MMAP_LEN_OFFSET     44
+#define MB_INFO_MMAP_ADDR_OFFSET    48
+#define MB_MMAP_SIZE                480  /* 20 * sizeof(memory_map_t) - up to 20 entries
*/
 #define CMDLINE_SIZE                256
 /*
@@ -91,6 +95,35 @@ MultibootEntry:
     cmp eax, MULTIBOOT_BOOTLOADER_MAGIC
     jne mbfail
+    /* Save multiboot info structure */
+    mov esi, ebx
+    mov edi, offset MultibootInfo + INITIAL_BASE - FREELDR_BASE
+    mov ecx, (MB_INFO_SIZE / 4)
+    rep movsd
+    mov dword ptr ds:[MultibootInfo + INITIAL_BASE - FREELDR_BASE +
MB_INFO_MMAP_ADDR_OFFSET], 0
+    mov dword ptr ds:[_MultibootInfoPtr + INITIAL_BASE - FREELDR_BASE], offset
MultibootInfo
+
+    /* See if the memory map was passed in */
+    test dword ptr ds:[ebx + MB_INFO_FLAGS_OFFSET], MB_INFO_FLAG_MEMORY_MAP
+    jz mbchk_command_line
+    /* Check memory map length */
+    mov ecx, dword ptr ds:[ebx + MB_INFO_MMAP_LEN_OFFSET]
+    test ecx, ecx
+    jz mbchk_command_line
+    cmp ecx, MB_MMAP_SIZE
+    jg mbchk_command_line
+    /* Check memory map address */
+    mov esi, dword ptr ds:[ebx + MB_INFO_MMAP_ADDR_OFFSET]
+    test esi, esi
+    jz mbchk_command_line
+    /* Save memory map structure */
+    mov edi, offset MultibootMemoryMap + INITIAL_BASE - FREELDR_BASE
+    shr ecx, 2
+    rep movsd
+    /* Relocate memory map address */
+    mov dword ptr ds:[MultibootInfo + INITIAL_BASE - FREELDR_BASE +
MB_INFO_MMAP_ADDR_OFFSET], offset MultibootMemoryMap
+
+mbchk_command_line:
     /* Save command line */
     test dword ptr ds:[ebx + MB_INFO_FLAGS_OFFSET], MB_INFO_FLAG_COMMAND_LINE
     jz mb2
@@ -171,6 +204,16 @@ gdtptr:
     .word HEX(17)   /* Limit */
     .long gdt       /* Base Address */
+PUBLIC _MultibootInfoPtr
+_MultibootInfoPtr:
+    .long 0
+
+MultibootInfo:
+    .space MB_INFO_SIZE
+
+MultibootMemoryMap:
+    .space MB_MMAP_SIZE
+
 PUBLIC cmdline
 cmdline:
     .space CMDLINE_SIZE
diff --git a/boot/freeldr/freeldr/arch/i386/xboxmem.c
b/boot/freeldr/freeldr/arch/i386/xboxmem.c
index bf7721dc32c..e8ea7b016ea 100644
--- a/boot/freeldr/freeldr/arch/i386/xboxmem.c
+++ b/boot/freeldr/freeldr/arch/i386/xboxmem.c
@@ -26,6 +26,7 @@ DBG_DEFAULT_CHANNEL(MEMORY);
 static ULONG InstalledMemoryMb = 0;
 static ULONG AvailableMemoryMb = 0;
+extern multiboot_info_t * MultibootInfoPtr;
 extern PVOID FrameBuffer;
 extern ULONG FrameBufferSize;
@@ -98,30 +99,107 @@ XboxMemInit(VOID)
     AvailableMemoryMb = InstalledMemoryMb;
 }
+memory_map_t *
+XboxGetMultibootMemoryMap(INT * Count)
+{
+    memory_map_t * MemoryMap;
+
+    if (!MultibootInfoPtr)
+    {
+        ERR("Multiboot info structure not found!\n");
+        return NULL;
+    }
+
+    if (!(MultibootInfoPtr->flags & MB_INFO_FLAG_MEMORY_MAP))
+    {
+        ERR("Multiboot memory map is not passed!\n");
+        return NULL;
+    }
+
+    MemoryMap = (memory_map_t *)MultibootInfoPtr->mmap_addr;
+
+    if (!MemoryMap ||
+        MultibootInfoPtr->mmap_length == 0 ||
+        MultibootInfoPtr->mmap_length % sizeof(memory_map_t) != 0)
+    {
+        ERR("Multiboot memory map structure is malformed!\n");
+        return NULL;
+    }
+
+    *Count = MultibootInfoPtr->mmap_length / sizeof(memory_map_t);
+    return MemoryMap;
+}
+
+TYPE_OF_MEMORY
+XboxMultibootMemoryType(ULONG Type)
+{
+    switch (Type)
+    {
+        case 0: // Video RAM
+            return LoaderFirmwarePermanent;
+        case 1: // Available RAM
+            return LoaderFree;
+        case 3: // ACPI area
+            return LoaderFirmwareTemporary;
+        case 4: // Hibernation area
+            return LoaderSpecialMemory;
+        case 5: // Reserved or invalid memory
+            return LoaderSpecialMemory;
+        default:
+            return LoaderFirmwarePermanent;
+    }
+}
+
 FREELDR_MEMORY_DESCRIPTOR XboxMemoryMap[MAX_BIOS_DESCRIPTORS + 1];
 PFREELDR_MEMORY_DESCRIPTOR
 XboxMemGetMemoryMap(ULONG *MemoryMapSize)
 {
+    memory_map_t * MbMap;
+    INT Count, i;
+
     TRACE("XboxMemGetMemoryMap()\n");
-    /* FIXME: Obtain memory map via multiboot spec */
-    /* Synthesize memory map */
+    MbMap = XboxGetMultibootMemoryMap(&Count);
+    if (MbMap)
+    {
+        /* Obtain memory map via multiboot spec */
-    /* Available RAM block */
-    SetMemory(XboxMemoryMap,
-              0,
-              AvailableMemoryMb * 1024 * 1024,
-              LoaderFree);
+        for (i = 0; i < Count; i++, MbMap++)
+        {
+            TRACE("i = %d, base_addr_low = 0x%p, length_low = 0x%p\n", i,
MbMap->base_addr_low, MbMap->length_low);
-    if (FrameBufferSize != 0)
+            if (MbMap->base_addr_high > 0 || MbMap->length_high > 0)
+            {
+                ERR("Memory descriptor base or size is greater than 4 GB, should not
happen on Xbox!\n");
+                ASSERT(FALSE);
+            }
+
+            SetMemory(XboxMemoryMap,
+                      MbMap->base_addr_low,
+                      MbMap->length_low,
+                      XboxMultibootMemoryType(MbMap->type));
+        }
+    }
+    else
     {
-        /* Video memory */
-        ReserveMemory(XboxMemoryMap,
-                      (ULONG_PTR)FrameBuffer,
-                      FrameBufferSize,
-                      LoaderFirmwarePermanent,
-                      "Video memory");
+        /* Synthesize memory map */
+
+        /* Available RAM block */
+        SetMemory(XboxMemoryMap,
+                  0,
+                  AvailableMemoryMb * 1024 * 1024,
+                  LoaderFree);
+
+        if (FrameBufferSize != 0)
+        {
+            /* Video memory */
+            ReserveMemory(XboxMemoryMap,
+                          (ULONG_PTR)FrameBuffer,
+                          FrameBufferSize,
+                          LoaderFirmwarePermanent,
+                          "Video memory");
+        }
     }
     *MemoryMapSize = PcMemFinalizeMemoryMap(XboxMemoryMap);
diff --git a/boot/freeldr/freeldr/arch/i386/xboxvideo.c
b/boot/freeldr/freeldr/arch/i386/xboxvideo.c
index d5839024d43..ffc2144d408 100644
--- a/boot/freeldr/freeldr/arch/i386/xboxvideo.c
+++ b/boot/freeldr/freeldr/arch/i386/xboxvideo.c
@@ -30,6 +30,7 @@ static ULONG ScreenWidth;
 static ULONG ScreenHeight;
 static ULONG BytesPerPixel;
 static ULONG Delta;
+extern multiboot_info_t * MultibootInfoPtr;
 #define CHAR_WIDTH  8
 #define CHAR_HEIGHT 16
@@ -127,6 +128,46 @@ NvGetCrtc(UCHAR Index)
     return *((PUCHAR) NV2A_CRTC_REGISTER_VALUE);
 }
+ULONG
+XboxGetFramebufferSize(PVOID Offset)
+{
+    memory_map_t * MemoryMap;
+    INT Count, i;
+
+    if (!MultibootInfoPtr)
+    {
+        return 0;
+    }
+
+    if (!(MultibootInfoPtr->flags & MB_INFO_FLAG_MEMORY_MAP))
+    {
+        return 0;
+    }
+
+    MemoryMap = (memory_map_t *)MultibootInfoPtr->mmap_addr;
+
+    if (!MemoryMap ||
+        MultibootInfoPtr->mmap_length == 0 ||
+        MultibootInfoPtr->mmap_length % sizeof(memory_map_t) != 0)
+    {
+        return 0;
+    }
+
+    Count = MultibootInfoPtr->mmap_length / sizeof(memory_map_t);
+    for (i = 0; i < Count; i++, MemoryMap++)
+    {
+        TRACE("i = %d, base_addr_low = 0x%p, MemoryMap->length_low =
0x%p\n", i, MemoryMap->base_addr_low, MemoryMap->length_low);
+
+        if (MemoryMap->base_addr_low == (ULONG)Offset &&
MemoryMap->base_addr_high == 0)
+        {
+            TRACE("Video memory found\n");
+            return MemoryMap->length_low;
+        }
+    }
+    ERR("Video memory not found!\n");
+    return 0;
+}
+
 VOID
 XboxVideoInit(VOID)
 {
@@ -135,8 +176,13 @@ XboxVideoInit(VOID)
   /* Verify that framebuffer address is page-aligned */
   ASSERT((ULONG_PTR)FrameBuffer % PAGE_SIZE == 0);
-  /* FIXME: obtain fb size from firmware somehow (Cromwell reserves high 4 MB of RAM) */
-  FrameBufferSize = 4 * 1024 * 1024;
+  /* Obtain framebuffer memory size from multiboot memory map */
+  if ((FrameBufferSize = XboxGetFramebufferSize(FrameBuffer)) == 0)
+  {
+    /* Fallback to Cromwell standard which reserves high 4 MB of RAM */
+    FrameBufferSize = 4 * 1024 * 1024;
+    WARN("Could not detect framebuffer memory size, fallback to 4 MB\n");
+  }
   ScreenWidth = *((PULONG) NV2A_RAMDAC_FP_HVALID_END) + 1;
   ScreenHeight = *((PULONG) NV2A_RAMDAC_FP_VVALID_END) + 1;
diff --git a/boot/freeldr/freeldr/include/multiboot.h
b/boot/freeldr/freeldr/include/multiboot.h
index 732e5c89b53..b5627d6db89 100644
--- a/boot/freeldr/freeldr/include/multiboot.h
+++ b/boot/freeldr/freeldr/include/multiboot.h
@@ -90,6 +90,27 @@ typedef struct elf_section_header_table
   unsigned long shndx;
 } elf_section_header_table_t;
+/* The Multiboot information. */
+typedef struct multiboot_info
+{
+  unsigned long flags;
+  unsigned long mem_lower;
+  unsigned long mem_upper;
+  unsigned long boot_device;
+  unsigned long cmdline;
+  unsigned long mods_count;
+  unsigned long mods_addr;
+  union
+  {
+    aout_symbol_table_t aout_sym;
+    elf_section_header_table_t elf_sec;
+  } u;
+  unsigned long mmap_length;
+  unsigned long mmap_addr;
+  unsigned long drives_length;
+  unsigned long drives_addr;
+} multiboot_info_t;
+
 /* The memory map. Be careful that the offset 0 is base_addr_low
    but no size. */
 typedef struct memory_map