[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-information-format

CORE-16216 CORE-16300
This commit is contained in:
Stanislav Motylkov 2019-12-31 18:10:34 +03:00 committed by Hermès BÉLUSCA - MAÏTO
parent 7523a7b138
commit 96692636e4
4 changed files with 206 additions and 18 deletions

View file

@ -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

View file

@ -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 */
/* Available RAM block */
SetMemory(XboxMemoryMap,
0,
AvailableMemoryMb * 1024 * 1024,
LoaderFree);
if (FrameBufferSize != 0)
MbMap = XboxGetMultibootMemoryMap(&Count);
if (MbMap)
{
/* Video memory */
ReserveMemory(XboxMemoryMap,
(ULONG_PTR)FrameBuffer,
FrameBufferSize,
LoaderFirmwarePermanent,
"Video memory");
/* Obtain memory map via multiboot spec */
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 (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
{
/* 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);

View file

@ -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;

View file

@ -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