/** @file
This file Multiboot-2 specification (implementation).
Copyright (c) 2014 - 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "MultibootLibInternal.h"
#include "multiboot2.h"
VOID DumpMb2Header (CONST struct multiboot2_header *Mh);
/**
Get multiboot header address
Get multiboot header following Multboot spec 2.0.
@param[in] ImageAddr Memory address of an image
@retval MultibootHeader Pointer to Multiboot header if found.
@retval NULL No valid Multiboot header found or requesting
features not support.
**/
CONST struct multiboot2_header *
EFIAPI
GetMultiboot2Header (
IN VOID *ImageAddr
)
{
UINT32 AlignedAddress;
struct multiboot2_header *MbHeader;
UINT32 Offset;
// Multiboot header must be 64-bit aligned.
AlignedAddress = ALIGN_UP((UINT32)(UINTN)ImageAddr, MULTIBOOT2_HEADER_ALIGN);
// Multiboot header must completely within the first 32K bytes of the image.
for (Offset = 0; Offset < MULTIBOOT2_SEARCH - sizeof (struct multiboot2_header); Offset += MULTIBOOT2_HEADER_ALIGN) {
MbHeader = (struct multiboot2_header *)(UINTN)(AlignedAddress + Offset);
if ((MbHeader->magic == MULTIBOOT2_HEADER_MAGIC) &&
(MbHeader->architecture == MULTIBOOT2_ARCHITECTURE_I386) &&
(MbHeader->magic + MbHeader->architecture + MbHeader->header_length + MbHeader->checksum == 0)) {
return (CONST struct multiboot2_header *) MbHeader;
}
}
return NULL;
}
/**
Check if it is Multiboot image
@param[in] ImageAddr Memory address of an image
@retval TRUE Image is Multiboot 0.6.98 compliant image
@retval FALSE Not multiboot image
**/
BOOLEAN
EFIAPI
IsMultiboot2 (
IN VOID *ImageAddr
)
{
CONST struct multiboot2_header *MbHeader;
MbHeader = GetMultiboot2Header (ImageAddr);
if (MbHeader != NULL) {
DumpMb2Header (MbHeader);
return TRUE;
}
return FALSE;
}
/**
Get memory map information.
@retval Memory Map Info pointer
**/
STATIC
MEMORY_MAP_INFO *
EFIAPI
GetMemoryMapInfo (
VOID
)
{
EFI_HOB_GUID_TYPE *GuidHob;
GuidHob = GetNextGuidHob (&gLoaderMemoryMapInfoGuid, GetHobList());
if (GuidHob == NULL) {
ASSERT (GuidHob);
return NULL;
}
return (MEMORY_MAP_INFO *)GET_GUID_HOB_DATA (GuidHob);
}
/**
Init Multiboot-2 memory map.
@param[out] MbMmap Multiboot memmap buffer
@param[in] MemoryMapInfo Memmap buffer from boot loader
**/
VOID
InitMultiboot2Mmap (
OUT struct multiboot2_mmap_entry *MbMmap,
IN MEMORY_MAP_INFO *MemoryMapInfo
)
{
UINT32 Index;
MEMORY_MAP_ENTRY *MmapEntry;
MmapEntry = &MemoryMapInfo->Entry[0];
for (Index = 0; Index < MemoryMapInfo->Count; Index++) {
MbMmap[Index].addr = MmapEntry[Index].Base;
MbMmap[Index].len = MmapEntry[Index].Size;
MbMmap[Index].type = MmapEntry[Index].Type;
MbMmap[Index].zero = 0;
}
}
/**
Setup the Multiboot info for boot usage.
@param[in,out] MultiBoot Point to loaded Multiboot image structure
**/
VOID
EFIAPI
SetupMultiboot2Info (
IN OUT MULTIBOOT_IMAGE *MultiBoot
)
{
MULTIBOOT2_INFO *Mb2Info = &MultiBoot->Mb2Info;
unsigned mb2_info_size_max = 0x1000;
void *mb2_info = AllocatePool (mb2_info_size_max);
if (mb2_info == NULL) {
DEBUG ((DEBUG_INFO, "Multiboot-2 info buffer allocation Error\n"));
ASSERT (mb2_info);
}
struct multiboot2_start_tag *mbi_start = (struct multiboot2_start_tag *)mb2_info;
Mb2Info->StartTag = mbi_start;
unsigned info_idx = sizeof(*mbi_start);
/*
** Add tags:
*/
#define DECLARE_TAG(STRUCT, TAG, BASE, P) \
STRUCT *TAG = (STRUCT *) ((char *)(BASE) + (P))
#define APPEND_TAG(P, TAG, TYPE, SIZE) do { \
(TAG)->type = (TYPE); \
(TAG)->size = (SIZE); \
(P) += ALIGN_UP((TAG)->size, MULTIBOOT2_TAG_ALIGN); \
} while (0)
// command line tag (1)
if (MultiBoot->CmdFile.Size != 0)
{
DECLARE_TAG (struct multiboot2_tag_string, tag, mb2_info, info_idx);
unsigned len = MultiBoot->CmdFile.Size;
CopyMem(tag->string, MultiBoot->CmdFile.Addr, len);
tag->string[len] = '\0';
APPEND_TAG (info_idx, tag, MULTIBOOT2_TAG_TYPE_CMDLINE, sizeof(struct multiboot2_tag_string) + len + 1);
}
// bootloader name tag (2)
{
DECLARE_TAG (struct multiboot2_tag_string, tag, mb2_info, info_idx);
UINT32 len = (UINT32) AsciiStrSize((CHAR8 *) mLoaderName);
CopyMem(tag->string, mLoaderName, len);
APPEND_TAG (info_idx, tag, MULTIBOOT2_TAG_TYPE_BOOT_LOADER_NAME, sizeof(struct multiboot2_tag_string) + len);
}
// module tags (3)
for (unsigned idx = 0; idx < MultiBoot->MbModuleNumber; idx += 1)
{
DECLARE_TAG (struct multiboot2_tag_module, module_tag, mb2_info, info_idx);
const MULTIBOOT_MODULE *mod = &MultiBoot->MbModule[idx];
UINT32 len = (UINT32) AsciiStrSize((CHAR8 *) mod->String);
module_tag->mod_start = mod->Start;
module_tag->mod_end = mod->End;
CopyMem(module_tag->cmdline, mod->String, len);
APPEND_TAG (info_idx, module_tag, MULTIBOOT2_TAG_TYPE_MODULE, sizeof(struct multiboot2_tag_module) + len);
}
// basic memory info tag (4)
{
DECLARE_TAG (struct multiboot2_tag_basic_meminfo, tag, mb2_info, info_idx);
// The amount of lower and upper memory size (in KB) will be updated
// later since current memmap is not the final memmap.
tag->mem_upper = 0;
tag->mem_lower = 0;
APPEND_TAG (info_idx, tag, MULTIBOOT2_TAG_TYPE_BASIC_MEMINFO, sizeof(struct multiboot2_tag_basic_meminfo));
}
// normal memory map tag (6)
{
DECLARE_TAG (struct multiboot2_tag_mmap, mmap_tag, mb2_info, info_idx);
MEMORY_MAP_INFO *mmap_info = GetMemoryMapInfo();
unsigned mb_mmap_count = mmap_info->Count;
mmap_tag->entry_size = sizeof(struct multiboot2_mmap_entry);
mmap_tag->entry_version = 0;
InitMultiboot2Mmap (&mmap_tag->entries[0], mmap_info);
APPEND_TAG (info_idx, mmap_tag, MULTIBOOT2_TAG_TYPE_MMAP,
sizeof(struct multiboot2_tag_mmap) + sizeof(struct multiboot2_mmap_entry) * mb_mmap_count);
}
// framebuffer tag (8)
EFI_HOB_GUID_TYPE *GuidHob = GetFirstGuidHob (&gEfiGraphicsInfoHobGuid);
if (GuidHob != NULL)
{
DECLARE_TAG (struct multiboot2_tag_framebuffer, tag, mb2_info, info_idx);
EFI_PEI_GRAPHICS_INFO_HOB *GfxInfoHob = (EFI_PEI_GRAPHICS_INFO_HOB *)GET_GUID_HOB_DATA (GuidHob);
tag->common.framebuffer_addr = (UINT64)(UINTN)GfxInfoHob->FrameBufferBase;
tag->common.framebuffer_pitch = GfxInfoHob->GraphicsMode.PixelsPerScanLine * 4;
tag->common.framebuffer_width = GfxInfoHob->GraphicsMode.HorizontalResolution;
tag->common.framebuffer_height = GfxInfoHob->GraphicsMode.VerticalResolution;
tag->common.framebuffer_bpp = 32;
tag->common.framebuffer_type = 1;
tag->u.type1.framebuffer_red_field_position = 0x10;
tag->u.type1.framebuffer_red_mask_size = 8;
tag->u.type1.framebuffer_green_field_position = 0x08;
tag->u.type1.framebuffer_green_mask_size = 8;
tag->u.type1.framebuffer_blue_field_position = 0x00;
tag->u.type1.framebuffer_blue_mask_size = 8;
APPEND_TAG (info_idx, &tag->common, MULTIBOOT2_TAG_TYPE_FRAMEBUFFER, sizeof(struct multiboot2_tag_framebuffer));
}
// end tag (0)
{
DECLARE_TAG (struct multiboot2_tag, end_tag, mb2_info, info_idx);
APPEND_TAG (info_idx, end_tag, MULTIBOOT2_TAG_TYPE_END, sizeof(struct multiboot2_tag));
}
#undef DECLARE_TAG
#undef APPEND_TAG
ASSERT (info_idx <= mb2_info_size_max);
mbi_start->size = info_idx;
mbi_start->reserved = 0x00;
/*
** Arrange for passing this data to the image.
*/
MultiBoot->BootState.Eax = MULTIBOOT2_BOOTLOADER_MAGIC;
MultiBoot->BootState.Ebx = (UINT32)(UINTN)mbi_start;
}
/**
Update the memory info inside the Multiboot-2 info.
@param[in,out] MultiBoot Point to loaded Multiboot-2 image structure
@param[in] RsvdMemBase Reserved memory base address
@param[in] RsvdMemSize Reserved memory size
@param[in] RsvdMemExtra Extra space to add to the reserved memory region.
**/
VOID
EFIAPI
UpdateMultiboot2MemInfo (
IN OUT MULTIBOOT_IMAGE *MultiBoot,
IN UINT64 RsvdMemBase,
IN UINT64 RsvdMemSize,
IN UINT32 RsvdMemExtra
)
{
struct multiboot2_start_tag *Mi = MultiBoot->Mb2Info.StartTag;
struct multiboot2_tag_basic_meminfo *minfo_tag = NULL;
struct multiboot2_tag_mmap *mmap_tag = NULL;
//
// Find the BASIC_MEMINFO and MMAP info tags; punt if not found (we created them).
//
for (unsigned off = sizeof(*Mi); off < Mi->size; ) {
struct multiboot2_tag *tag = (void *) ((char *) Mi + off);
if (tag->type == MULTIBOOT2_TAG_TYPE_BASIC_MEMINFO)
minfo_tag = (struct multiboot2_tag_basic_meminfo *) tag;
if (tag->type == MULTIBOOT2_TAG_TYPE_MMAP)
mmap_tag = (struct multiboot2_tag_mmap *) tag;
if (tag->type == MULTIBOOT2_TAG_TYPE_END)
break;
off += ALIGN_UP (MAX (tag->size, sizeof(*tag)), MULTIBOOT2_TAG_ALIGN);
}
if (minfo_tag == NULL || mmap_tag == NULL)
ASSERT (minfo_tag && mmap_tag);
//
// Iterate over the memory map entries and look for:
// - the given reserved region => extend it by the reqested extra size (from the beginning);
// - the region that is immediately below the reserved space => shrink it by the extra space;
// - usable memory regions starting at address 0x0 or 1M => record size in the basic mem info tag.
//
unsigned n_entries = (mmap_tag->size - sizeof(*mmap_tag)) / mmap_tag->entry_size;
for (unsigned idx = 0; idx < n_entries; idx += 1) {
struct multiboot2_mmap_entry *entry = &mmap_tag->entries[idx];
DEBUG ((DEBUG_INFO, "\n\t%12llx %12llx %4d",
mmap_tag->entries[idx].addr,
mmap_tag->entries[idx].len,
mmap_tag->entries[idx].type));
if (entry->addr == RsvdMemBase && entry->len == RsvdMemSize) {
entry->addr -= RsvdMemExtra;
entry->len += RsvdMemExtra;
} else if (entry->addr + entry->len == RsvdMemBase) {
entry->len -= RsvdMemExtra;
}
if (entry->type == MEM_MAP_TYPE_RAM) {
if (entry->addr == 0)
minfo_tag->mem_lower = (UINT32) (RShiftU64 (entry->len, 10));
else if (entry->addr == MB_ (1))
minfo_tag->mem_upper = (UINT32) (RShiftU64 (entry->len, 10));
}
}
}
/**
Parse Multiboot-2 image header tags and extract data required for setting up the image.
@param[in] Mh The Multiboot header to be parsed.
@retval RETURN_SUCCESS All required information was found.
@retval Others Some required information is not present.
**/
EFI_STATUS
EFIAPI
ParseMultiboot2Header (
IN CONST struct multiboot2_header *Mh,
OUT BOOLEAN *AlignModules,
OUT UINT8 **HeaderAddr,
OUT UINT8 **LoadAddr,
OUT UINT8 **LoadEnd,
OUT UINT8 **BssEnd,
OUT UINT32 *EntryPoint
)
{
*AlignModules = FALSE;
UINT32 tags_needed = (1U << MULTIBOOT2_HEADER_TAG_ADDRESS)
| (1U << MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS);
for (unsigned off = sizeof(*Mh); off < Mh->header_length ; )
{
const struct multiboot2_header_tag *htag = (void *) ((char *) Mh + off);
switch (htag->type)
{
case MULTIBOOT2_HEADER_TAG_ADDRESS: // 2
{
const struct multiboot2_header_tag_address *tag = (void *) ((char *) Mh + off);
*HeaderAddr = (UINT8 *)(UINTN) tag->header_addr;
*LoadAddr = (UINT8 *)(UINTN) tag->load_addr;
*LoadEnd = (UINT8 *)(UINTN) tag->load_end_addr;
*BssEnd = (UINT8 *)(UINTN) tag->bss_end_addr;
}
break;
case MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS: // 3
{
const struct multiboot2_header_tag_entry_address *tag = (void *) ((char *) Mh + off);
*EntryPoint = tag->entry_addr;
}
break;
case MULTIBOOT2_HEADER_TAG_MODULE_ALIGN: // 6
*AlignModules = TRUE;
break;
case MULTIBOOT2_HEADER_TAG_END: // 0
case MULTIBOOT2_HEADER_TAG_FRAMEBUFFER: // 5
// noted, ignore the tag.
break;
// case MULTIBOOT2_HEADER_TAG_INFORMATION_REQUEST: // 1
// case MULTIBOOT2_HEADER_TAG_CONSOLE_FLAGS: // 4
// case MULTIBOOT2_HEADER_TAG_EFI_BS: // 7
// case MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS_EFI32: // 8
// case MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS_EFI64: // 9
// case MULTIBOOT2_HEADER_TAG_RELOCATABLE: // 10
default:
// unsupported or unknown tag, failure!
return RETURN_UNSUPPORTED;
}
off += ALIGN_UP (MAX (htag->size, sizeof(*htag)), MULTIBOOT2_TAG_ALIGN);
if (htag->type == MULTIBOOT2_TAG_TYPE_END)
break;
tags_needed &= ~(1U << htag->type);
}
return (tags_needed ? RETURN_UNSUPPORTED : RETURN_SUCCESS);
}
/**
Align multiboot modules if requested by module alignment tag.
@param[in,out] MultiBoot Point to loaded Multiboot image structure
@retval RETURN_SUCCESS Align modules successfully
@retval Others There is error when align image
**/
EFI_STATUS
EFIAPI
CheckAndAlignMultiboot2Modules (
IN OUT MULTIBOOT_IMAGE *MultiBoot
)
{
EFI_STATUS Status;
BOOLEAN AlignModules;
UINT8 *HeaderAddr;
UINT8 *LoadAddr;
UINT8 *LoadEnd;
UINT8 *BssEnd;
UINT32 EntryPoint;
CONST struct multiboot2_header *MbHeader;
if (MultiBoot == NULL) {
return RETURN_INVALID_PARAMETER;
}
MbHeader = GetMultiboot2Header (MultiBoot->BootFile.Addr);
if (MbHeader == NULL) {
return RETURN_LOAD_ERROR;
}
Status = ParseMultiboot2Header (MbHeader, &AlignModules, &HeaderAddr, &LoadAddr, &LoadEnd, &BssEnd, &EntryPoint);
if (EFI_ERROR (Status)) {
return Status;
}
if (AlignModules) {
// Modules should be page (4KB) aligned
Status = AlignMultibootModules (MultiBoot);
if (EFI_ERROR (Status)) {
return Status;
}
}
return EFI_SUCCESS;
}
/**
Setup Multiboot image and its boot info.
@param[in,out] MultiBoot Point to loaded Multiboot image structure
@retval RETURN_SUCCESS Setup Multiboot image successfully
@retval Others There is error when setup image
**/
EFI_STATUS
EFIAPI
SetupMultiboot2Image (
IN OUT MULTIBOOT_IMAGE *MultiBoot
)
{
EFI_STATUS Status;
BOOLEAN AlignModules;
UINT8 *HeaderAddr;
UINT8 *LoadAddr;
UINT8 *LoadEnd;
UINT8 *BssEnd;
UINT32 EntryPoint;
CONST struct multiboot2_header *MbHeader;
UINT32 ImgOffset;
UINT32 ImgLength;
UINT8 *CopyStart;
if (MultiBoot == NULL) {
return RETURN_INVALID_PARAMETER;
}
MbHeader = GetMultiboot2Header (MultiBoot->BootFile.Addr);
if (MbHeader == NULL) {
return RETURN_LOAD_ERROR;
}
Status = ParseMultiboot2Header (MbHeader, &AlignModules, &HeaderAddr, &LoadAddr, &LoadEnd, &BssEnd, &EntryPoint);
if (EFI_ERROR (Status)) {
return Status;
}
if (AlignModules) {
// Other modules should be page (4KB) aligned
Status = AlignMultibootModules (MultiBoot);
if (EFI_ERROR (Status)) {
return Status;
}
}
ImgOffset = (UINT32)((UINT8 *)MbHeader - (UINT8 *)MultiBoot->BootFile.Addr - (HeaderAddr - LoadAddr));
ImgLength = (UINT32)((LoadEnd == NULL) ? MultiBoot->BootFile.Size - ImgOffset : LoadEnd - LoadAddr);
if ((ImgOffset >= MultiBoot->BootFile.Size) || (ImgOffset + ImgLength > MultiBoot->BootFile.Size)) {
return RETURN_LOAD_ERROR;
}
DEBUG ((DEBUG_INFO, "Mb: LoadAddr=0x%p, LoadEnd=0x%p , BssEnd=0x%p, Size=0x%x\n", LoadAddr, LoadEnd, BssEnd, ImgLength));
CopyStart = (UINT8 *)MultiBoot->BootFile.Addr + ImgOffset;
CopyMem (LoadAddr, CopyStart, ImgLength);
if ((BssEnd != NULL) && (LoadEnd != NULL)) {
if (BssEnd > LoadEnd) {
ZeroMem ((VOID *) LoadEnd, BssEnd - LoadEnd);
}
}
SetupMultiboot2Info (MultiBoot);
MultiBoot->BootState.EntryPoint = EntryPoint;
return EFI_SUCCESS;
}
/**
Print out the Multiboot header.
@param[in] Mh The Multiboot header to be printed.
**/
VOID
DumpMb2Header (CONST struct multiboot2_header *Mh)
{
DEBUG ((DEBUG_INFO, "\nDump MB2 header @%p:\n", Mh));
DEBUG ((DEBUG_INFO, "- magic: %8x\n", Mh->magic));
DEBUG ((DEBUG_INFO, "- architecture: %8x\n", Mh->architecture));
DEBUG ((DEBUG_INFO, "- header_length: %8x\n", Mh->header_length));
DEBUG ((DEBUG_INFO, "- checksum: %8x\n", Mh->checksum));
for (unsigned off = sizeof(*Mh); off < Mh->header_length ; )
{
const struct multiboot2_header_tag *htag = (void *) ((char *) Mh + off);
DEBUG ((DEBUG_INFO, "- tag @%02x [T=%2d F=%04x S=%2d]: ", off, htag->type, htag->flags, htag->size));
switch (htag->type)
{
case MULTIBOOT2_HEADER_TAG_END: // 0
DEBUG ((DEBUG_INFO, ""));
break;
case MULTIBOOT2_HEADER_TAG_ADDRESS: // 2
{
const struct multiboot2_header_tag_address *tag = (void *) ((char *) Mh + off);
DEBUG ((DEBUG_INFO, "header @%#x, load to %#x-%#x, bss-end @%#x",
tag->header_addr,
tag->load_addr,
tag->load_end_addr,
tag->bss_end_addr));
}
break;
case MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS: // 3
{
const struct multiboot2_header_tag_entry_address *tag = (void *) ((char *) Mh + off);
DEBUG ((DEBUG_INFO, "entry point: %#x", tag->entry_addr));
}
break;
case MULTIBOOT2_HEADER_TAG_MODULE_ALIGN: // 6
DEBUG ((DEBUG_INFO, "align modules."));
break;
case MULTIBOOT2_HEADER_TAG_FRAMEBUFFER: // 5
{
const struct multiboot2_header_tag_framebuffer *t5 = (void *) ((char *) Mh + off);
DEBUG ((DEBUG_INFO, "(frame buffer) preferred graphics mode: width=%d, height=%d, depth=%d",
t5->width, t5->height, t5->depth));
}
break;
// case MULTIBOOT2_HEADER_TAG_INFORMATION_REQUEST: // 1
// case MULTIBOOT2_HEADER_TAG_CONSOLE_FLAGS: // 4
// case MULTIBOOT2_HEADER_TAG_EFI_BS: // 7
// case MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS_EFI32: // 8
// case MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS_EFI64: // 9
// case MULTIBOOT2_HEADER_TAG_RELOCATABLE: // 10
default:
DEBUG ((DEBUG_INFO, "??"));
break;
}
DEBUG ((DEBUG_INFO, "\n"));
off += ALIGN_UP (MAX (htag->size, sizeof(*htag)), MULTIBOOT2_TAG_ALIGN);
if (htag->type == MULTIBOOT2_TAG_TYPE_END)
break;
}
}
/**
Print out the Multiboot-2 information block.
@param[in] Mi The Multiboot-2 information block to be printed.
**/
VOID
DumpMb2Info (
IN CONST struct multiboot2_start_tag *Mi
)
{
DEBUG ((DEBUG_INFO, "\nDump MB2 info @%p (size=%d, reserved=%x):\n", Mi, Mi->size, Mi->reserved));
if (Mi == NULL) {
return;
}
for (unsigned off = sizeof(*Mi); off < Mi->size; )
{
const struct multiboot2_tag *tag = (void *) ((char *) Mi + off);
DEBUG ((DEBUG_INFO, "- tag @%03x [T=%2d S=%2d]: ", off, tag->type, tag->size));
switch (tag->type)
{
case MULTIBOOT2_TAG_TYPE_END: // 0
DEBUG ((DEBUG_INFO, ""));
break;
case MULTIBOOT2_TAG_TYPE_CMDLINE: // 1
{
struct multiboot2_tag_string *string_tag = (void *) ((char *) Mi + off);
DEBUG ((DEBUG_INFO, "command line: '%a'",
string_tag->string));
}
break;
case MULTIBOOT2_TAG_TYPE_BOOT_LOADER_NAME: // 2
{
struct multiboot2_tag_string *string_tag = (void *) ((char *) Mi + off);
DEBUG ((DEBUG_INFO, "boot loader name: '%a'",
string_tag->string));
}
break;
case MULTIBOOT2_TAG_TYPE_MODULE: // 3
{
struct multiboot2_tag_module *module_tag = (void *) ((char *) Mi + off);
DEBUG ((DEBUG_INFO, "module: start %#x, end %#x, cmd line '%a'",
module_tag->mod_start, module_tag->mod_end, module_tag->cmdline));
}
break;
case MULTIBOOT2_TAG_TYPE_BASIC_MEMINFO: // 4
{
struct multiboot2_tag_basic_meminfo *meminfo_tag = (void *) ((char *) Mi + off);
DEBUG ((DEBUG_INFO, "basic memory info: %dK lower, %dK upper memory",
meminfo_tag->mem_lower, meminfo_tag->mem_upper));
}
break;
case MULTIBOOT2_TAG_TYPE_MMAP: // 6
{
struct multiboot2_tag_mmap *mmap_tag = (void *) ((char *) Mi + off);
unsigned n_entries = (mmap_tag->size - sizeof(*mmap_tag)) / mmap_tag->entry_size;
DEBUG ((DEBUG_INFO, "memory map: %d entries (version=%d, size=%d):",
n_entries, mmap_tag->entry_version, mmap_tag->entry_size));
DEBUG ((DEBUG_INFO, "\n\t address length type\n\t------------ ------------ ----"));
for (unsigned idx = 0; idx < n_entries; idx += 1)
{
DEBUG ((DEBUG_INFO, "\n\t%12llx %12llx %4d",
mmap_tag->entries[idx].addr,
mmap_tag->entries[idx].len,
mmap_tag->entries[idx].type));
}
}
break;
case MULTIBOOT2_TAG_TYPE_FRAMEBUFFER: // 8
{
struct multiboot2_tag_framebuffer *fb_tag = (void *) ((char *) Mi + off);
DEBUG ((DEBUG_INFO, "frame buffer @%llx: pitch %#x, width %d, height %d, bpp %d\n\ttype %d",
fb_tag->common.framebuffer_addr,
fb_tag->common.framebuffer_pitch,
fb_tag->common.framebuffer_width,
fb_tag->common.framebuffer_height,
fb_tag->common.framebuffer_bpp,
fb_tag->common.framebuffer_type));
if (fb_tag->common.framebuffer_type == MULTIBOOT2_FRAMEBUFFER_TYPE_INDEXED)
DEBUG ((DEBUG_INFO, " (indexed, %d colors): ...",
fb_tag->u.type0.framebuffer_palette_num_colors));
else if (fb_tag->common.framebuffer_type == MULTIBOOT2_FRAMEBUFFER_TYPE_RGB)
DEBUG ((DEBUG_INFO, " (RGB): R=(%d,%d) G=(%d,%d) B=(%d,%d)",
fb_tag->u.type1.framebuffer_red_field_position, fb_tag->u.type1.framebuffer_red_mask_size,
fb_tag->u.type1.framebuffer_green_field_position, fb_tag->u.type1.framebuffer_green_mask_size,
fb_tag->u.type1.framebuffer_blue_field_position, fb_tag->u.type1.framebuffer_blue_mask_size));
else if (fb_tag->common.framebuffer_type == MULTIBOOT2_FRAMEBUFFER_TYPE_EGA_TEXT)
DEBUG ((DEBUG_INFO, ": EGA TEXT"));
else
DEBUG ((DEBUG_INFO, " ???"));
}
break;
// case MULTIBOOT2_TAG_TYPE_BOOTDEV: // 5
// case MULTIBOOT2_TAG_TYPE_VBE: // 7
// case MULTIBOOT2_TAG_TYPE_ELF_SECTIONS: // 9
// case MULTIBOOT2_TAG_TYPE_APM: // 10
// case MULTIBOOT2_TAG_TYPE_EFI32: // 11
// case MULTIBOOT2_TAG_TYPE_EFI64: // 12
// case MULTIBOOT2_TAG_TYPE_SMBIOS: // 13
// case MULTIBOOT2_TAG_TYPE_ACPI_OLD: // 14
// case MULTIBOOT2_TAG_TYPE_ACPI_NEW: // 15
// case MULTIBOOT2_TAG_TYPE_NETWORK: // 16
// case MULTIBOOT2_TAG_TYPE_EFI_MMAP: // 17
// case MULTIBOOT2_TAG_TYPE_EFI_BS: // 18
// case MULTIBOOT2_TAG_TYPE_EFI32_IH: // 19
// case MULTIBOOT2_TAG_TYPE_EFI64_IH: // 20
// case MULTIBOOT2_TAG_TYPE_LOAD_BASE_ADDR: // 21
default:
DEBUG ((DEBUG_INFO, "??"));
break;
}
DEBUG ((DEBUG_INFO, "\n"));
off += ALIGN_UP (MAX (tag->size, sizeof(*tag)), MULTIBOOT2_TAG_ALIGN);
if (tag->type == MULTIBOOT2_TAG_TYPE_END)
break;
}
}