我正在尝试为 i386 32 位编写一个非常基本的内核。我一直在关注 JamesM 的内核教程。我的内核代码本身目前做的很少 - 我正在努力让 grub 正确加载我选择使用的上半部分格式。

这样做的建议是 described in this answer ,我正在粗略地使用 higher half bare bones 。我的测试环境是 bochs,带有一个 grub 软盘(0.97/legacy)。

为了让您了解我在做什么,我有一个非常简单的几乎什么都不做的“主要”例程:

[section .text]
align 4

; setting up entry point for linker
start equ (start_vma - 0xBFF00000)

; entry point
start_vma:
    push ebx
    mov  eax, 0xCCCCCCCC
    hlt

这种技术给了我:
bjdump -x kernel | grep "start"
start address 0x00100000
c0000000 g       .text   00000000 start_vma
00100000 g       .text   00000000 start

然后我所做的是使用链接器脚本将 LMA(物理地址加载点)设置为 1MB,但将 VMA 设置为 3GB,如下所示:
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
KERNEL_MBOOT = 0x400;
KERNEL_VMA        = 0xC0000000;
KERNEL_LMA_OFFSET = 0xBFF00000;

PHDRS
{
    headers PT_PHDR PHDRS ;
    mboot PT_LOAD FILEHDR ;
    text PT_LOAD FILEHDR ;
    data PT_LOAD ;
}

SECTIONS
{
  . = KERNEL_MBOOT;

  .mboot : AT(KERNEL_VMA - KERNEL_LMA_OFFSET)
  {
     *(.mboot)
  } : mboot

  . = KERNEL_VMA;

  .text : AT(ADDR(.text)+ADDR(.mboot) - KERNEL_LMA_OFFSET)
  {
    code = .; _code = .; __code = .;*/
    *(.text)
    *(.rodata*)
  } : text

  .data ALIGN (0x1000) : AT(ADDR(.data) - KERNEL_LMA_OFFSET)
  {
     data = .; _data = .; __data = .;
     *(.data)
     *(.rodata)
  } : data

  .bss : AT(ADDR(.bss) - KERNEL_LMA_OFFSET)
  {
    bss = .; _bss = .; __bss = .;
    *(.bss)
    *(COMMON)
    ebss = .;
  } : data

  end = .; _end = .; __end = .;
}

或者至少我相信。 Explanation 。当然,这就是我的对象中的内容:
$ objdump -h kernel.bin
file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .mboot        0000000c  00000400  00100000  00000400  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .text         0000002e  c0000000  00100400  00001000  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .data         00000001  c0001000  00101000  00002000  2**12
                  CONTENTS, ALLOC, LOAD, DATA
  3 .bss          00004000  c0001020  00101020  00002001  2**5
                  ALLOC
.mboot 部分只包含多重引导头。事实上,运行 mbchk 会给出:
kernel: The Multiboot header is found at the offset 1024.
kernel: Page alignment is turned on.
kernel: Memory information is turned on.
kernel: Address fields is turned off.
kernel: All checks passed.

所以总而言之,这看起来是一个有效的,应该是引导 elf 内核,它什么都不做。

但是,它实际上并没有启动。相反,grub 会这样提示:



我在 x64 fedora 上使用 gcc 4.7.2 构建,使用:
CFLAGS=-nostdlib -fno-builtin -fno-stack-protector -ffreestanding -m32
LDFLAGS=-melf_i386 -Tlink.ld

其中 link.ld 是我上面包含的脚本。

我的问题是,我在上面做错了什么让 grub 相信内核可以放入内存?我努力了:
  • 更改 VMA_OFFSET 字段以使 VMA 与 LMA 匹配,即以 1MB 物理和虚拟加载所有内容没有区别,即我得到相同的错误。
  • 使 LMA 低于 1MB。导致 grub 错误 7:无法按预期加载低于 1MB。
  • Guzzle 茶和咖啡。

  • 这些事情都没有奏效。我有一种感觉,我错过了一些很明显的东西,但无法弄清楚它是什么。

    更新:查看 grub multiboot elf 行,这是我看到的:
     [Multiboot-elf, <0xffc00:0x40c:0x0>
    

    至关重要的是,我相信我应该看到类似于 this question 中的行,即它应该有一个 entry= 部分!

    无论如何,我看着提供一个初始化区域:
    [section .init]
    ; setting up entry point for linker
    
    ; entry point
    start:
        push ebx
        mov  eax, 0xCCCCCCCC
        ; jmp  later
        hlt
    

    我是这样链接的:
    .init : AT(ADDR(.mboot) + (KERNEL_VMA - KERNEL_LMA_OFFSET))
    {
        *(.init)
    } : init
    

    给予:
     Idx Name          Size      VMA       LMA       File off  Algn
     1   .init         0000000c  00100000  00100400  00001000  2**0
    

    它的 VMA/LMA 大约为 1M,所以这应该加载......但我仍然收到关于所选项目不适合内存的投诉。

    最佳答案

    在 grub 0.97 source code 中,此错误消息来自 stage2/common.c:89

    [ERR_WONT_FIT] = "Selected item cannot fit into memory",
    

    这段代码被使用了十几次:
    stage2/char_io.c:1215:    errnum = ERR_WONT_FIT;
    stage2/boot.c:276:    errnum = ERR_WONT_FIT;
    stage2/boot.c:280:  errnum = ERR_WONT_FIT;
    stage2/shared.h:540:  ERR_WONT_FIT,
    stage2/builtins.c:1822:     errnum = ERR_WONT_FIT;
    stage2/builtins.c:2386:      errnum = ERR_WONT_FIT;
    stage2/builtins.c:2498:      errnum = ERR_WONT_FIT;
    stage2/builtins.c:2594:   errnum = ERR_WONT_FIT;
    stage2/builtins.c:2932:   errnum = ERR_WONT_FIT;
    stage2/builtins.c:3711:   errnum = ERR_WONT_FIT;
    stage2/builtins.c:3744:   errnum = ERR_WONT_FIT;
    

    既然你在启动时有这个消息,让我们更深入地看看 boot.c
     if (! big_linux
          && text_len > linux_data_real_addr - (char *) LINUX_ZIMAGE_ADDR)
        {
          grub_printf (" linux 'zImage' kernel too big, try 'make bzImage'\n");
          errnum = ERR_WONT_FIT;
        }
     else if (linux_data_real_addr + LINUX_SETUP_MOVE_SIZE
                   > RAW_ADDR ((char *) (mbi.mem_lower << 10)))
            errnum = ERR_WONT_FIT;
    

    由于您没有 grub_printf 消息,因此您可能处于以下情况:
    linux_data_real_addr + LINUX_SETUP_MOVE_SIZE > RAW_ADDR ((char *) (mbi.mem_lower << 10))
    

    => 也许您的问题与您的 3 GB 目标有关。您可以尝试使其与 1GB 内存一起工作。计算机科学已经发现文件或内存 > 2GB 和 32 位 CPU 的许多问题。

    您找到它的另一个机会在于 stage2/char_io.c:1215
      if ((addr < RAW_ADDR (0x1000))
          || (addr < RAW_ADDR (0x100000)
              && RAW_ADDR (mbi.mem_lower * 1024) < (addr + len))
          || (addr >= RAW_ADDR (0x100000)
              && RAW_ADDR (mbi.mem_upper * 1024) < ((addr - 0x100000) + len)))
        errnum = ERR_WONT_FIT;
    

    您在 0x00100000 处的起始地址可能会触发此条件。

    关于assembly - Grub 错误 28 : selected item cannot fit into memory when writing higher half kernel,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17223834/

    10-14 08:04