assemblygccx86-64osdevgrub2

Multiboot2 traversing reveals invalid tags


While implementing a multiboot2 complaint OS booting with GRUB2, reading through the tags provided i have recieved type = 25 as well as my information request for type = 6 was ignored.

Not entirely sure if my method of traversing is valid.
No idea if this is a problem with GRUB2.
if it is correct, where is any information on type = 25.

;; multiboot2.asm

%define mb2_magic 0xE85250D6
%define mb2_arch 0

section .boot
_mb2:
dd mb2_magic
dd mb2_arch
dd _emb2 - _mb2
dd 0x100000000 - (mb2_magic + mb2_arch + (_emb2 - _mb2))

align 8

.mb2_ir:
    dw 1 ; information request
    dw 0
    dd .emb2_ir - .mb2_ir
    dd 6 ; memory map tag
.emb2_ir:

align 8

.end_tag:
    dd 0
    dd 8
_emb2:
;entry.asm

[BITS32]
__start:
    mov [.mb2_magic], eax
    mov [.mb2_info], ebx
    ;; ...
    call check_multiboot
    ;; ...
    lgdt [gdt64.ptr]
    jmp gdt64.code:realm64
    .mb2_magic:
        dd 0
    .mb2_info:
        dq 0

check_multiboot:
    mov eax, [__start.mb2_magic]
    cmp eax, 0x36D76289 ; multiboot2 magic number
    jne .fail
    ret
    .fail:
        mov ebx, .message
        call quick_print ; defined in file
        hlt
    .message:
        db "Boot Check Failed:", 0x0A, 0x0A, "Was not booted from MultiBoot2", 0x0A, "MBR (Master Boot Record) is incompatible", 0x00

;; ...
[BITS 64]
realm64:
    ;; ...
    [extern main]
    mov rsi, [__start.mb2_magic]
    mov rdi, [__start.mb2_info]
    call main
// kernel.c

int main(mb2_info_header* mbd, uint32_t magic) // magic checked in entry.asm
{
    tty_init();
    mb2_traverse(mbd);
    // ...
}
// multiboot2.c

typedef struct s_MB2_tag {
    uint32_t type;
    uint32_t size;
} mb2_tag;

typedef struct s_MB2_info_header {
    uint32_t total_size;
    uint32_t __RSV1;
} mb2_info_header;

void mb2_traverse(mb2_info_header *mbd)
{
    uint64_t mb2_end = mbd->total_size + (uint64_t)mbd;
    mb2_tag *tag = (mb2_tag*)(mbd + sizeof(mb2_info_header));
    while ((uint64_t)tag < mb2_end)
    {
        tty_printf("TAG %d\t[%d] @\t%x\n", tag->type, tag->size, tag);
        if (tag->type == 0)
            break;
        tag += tag->size;
        if ((uint64_t)tag & 0x7)
            tag += 8 - ((uint64_t)tag & 0x7);
    }
}

Yeilding:
TAG N [size] @ ptr
TTY screenshot


Solution

  • @MichaelPetch provided these comments (Huge Appreciation):

    Upon further inspection there is a similar casting issue (refering to previous comment) here as well: tag += tag->size; You could do something like tag = (mb2_tag *) ((uint8_t *) tag + ((tag->size + 7) & ~7)))

    When you add sizeof(mb2_info_header) (which is 8) to mbd you aren't adding 8 bytes to mbd, you are adding 8 * 8 since each element of mbd is 8 bytes already. If you add 1 to mbd you are actually adding 1 * 8.

    Realising that pointer arithmetic was the problem.

    After adjusting the code to this new traversal in multiboot2.c

    // multiboot2.c
    
    void mb2_traverse(mb2_info_header *mbd, uint32_t magic)
    {
        if (magic != 0x36D76289)
        {
            tty_printf("Multiboot2 Magic Number is invalid");
            asm volatile ("hlt");
        }
        tty_printf("MB2 [%d]\n", mbd->total_size);
        uint64_t mb2_end = mbd->total_size + (uint64_t)mbd;
    
        mb2_tag *tag = (mb2_tag*)((uint8_t *)mbd + sizeof(mb2_info_header)); // Adjusted to have (uint8_t *)
        while ((uint64_t)tag < mb2_end)
        {
            tty_printf("TAG %d\t[%d] @\t%x\n", tag->type, tag->size, tag);
            if (tag->type == 0)
                break;
            switch (tag->type) {
                // ...
                default: break;
            }
            tag = (mb2_tag *) ((uint8_t *) tag + ((tag->size + 7) & ~7)); // Adjusted to have (uint8_t *) and altered byte aligment
        }
    }
    

    Yeilds the results:
    Resulting Screenshot