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 */ - - /* 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); 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