无法在 VirtualBox 中启动自定义内核:“无法从启动介质中读取”

Posted

技术标签:

【中文标题】无法在 VirtualBox 中启动自定义内核:“无法从启动介质中读取”【英文标题】:Unable to boot a custom kernel in VirtualBox : "could not read from boot medium" 【发布时间】:2017-09-08 06:44:30 【问题描述】:

我按照Write your own operating system in 1 hour 上的教程系列创建了一个基本操作系统,仅打印“Hello World”,只有 4 个文件:Makefilekernel.cpploader.slinker.ld

我正在创建一个mykernel.iso 文件,但是当我将它启动到 VirtualBox 时,我收到错误“无法从启动介质读取:系统停止”。我确认.iso 文件与我的机器实例相关联。看起来代码中可能还有其他问题。

这是我的 Makefile:

#we need to tell the compiler to stop assuming that this will be executed inside an OS
CPPPARAMS = -m32 -fno-use-cxa-atexit -nostdlib -fno-builtin -fno-rtti -fno-exceptions -fno-leading-underscore
ASPARAMS = --32
LDPARAMS = -melf_i386

objects = loader.o kernel.o

%.o: %.cpp
    g++ $(CPPPARAMS) -o $@ -c $< 

%.o: %.s
    as $(ASPARAMS) -o $@ $<

mykernel.bin: linker.ld $(objects)
    ld $(LDPARAMS) -T $< -o $@ $(objects)

install: mykernel.bin
    sudo cp $< /boot/mykernel.bin

mykernel.iso: mykernel.bin
    mkdir iso
    mkdir iso/boot
    mkdir iso/boot/grub
    cp $< iso/boot/
    echo 'set default=0' > iso/boot/grub/grub.cfg
    echo 'set timeout=0' >> iso/boot/grub/grub.cfg
    echo '' >> iso/boot/grub/grub.cfg
    echo 'menuentry "My Personal OS" ' >> iso/boot/grub/grub.cfg
    echo 'multiboot /boot/mykernel.bin' >> iso/boot/grub/grub.cfg
    echo 'boot' >> iso/boot/grub/grub.cfg
    echo '' >> iso/boot/grub/grub.cfg
    grub-mkrescue --output $@ iso
    rm -rf iso

clean:
    rm -rf iso
    rm *.o
    rm mykernel.iso
    rm mykernel.bin

这里是kernel.cpp

void printf(char *str)

    unsigned short *VideoMemory = (unsigned short*)0xb8000;

    for(int i=0;str[i] != '\0';i++)
        VideoMemory[i] = (VideoMemory[i] & 0xFF00) | str[i];


typedef void (*constructor)();
extern "C" constructor start_ctors;
extern "C" constructor end_ctors;
extern "C" void callConstructors()

    for(constructor * i=&start_ctors;i!=&end_ctors;i++)
        (*i)();


extern "C" void kernelMain(void * multiboot_structure, unsigned int magic_number)

    printf("Hello World!");

    //we do not want to exit from the kernel
    while(1);

这里是loader.s

.set MAGIC, 0x1badb002
.set FLAGS, (1<<0 | 1<<1)
.set CHECKSUM, -(MAGIC + FLAGS)

.section .multiboot
    .long MAGIC
    .long FLAGS
    .long CHECKSUM

.section .text
.extern kernelMain
.extern callConstructors
.global loader

loader:
    mov $kernel_stack, %esp
    call callConstructors
    push %eax #AX register has the pointer of multiboot structure stored by bootloader
    push %ebx #BX register has the magic number
    call kernelMain

#double check to not come out of the kernel, creating one more loop
_stop:
    cli
    hlt
    jmp _stop


.section .bss
.space 2*1024*1024 #2MB  for stack to grow towards left side
kernel_stack:

这里是linker.ld

ENTRY(loader)
OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386:i386)

SECTIONS

    . = 0x100000;

    .text :
    
        *(.multiboot)
        *(.text*)
        *(.rodata)
    

    .data :
    
        start_ctors = .;
        KEEP(*(.init_array));
        KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)));
        end_ctors = .;

        *(.data)
    

    .bss :
    
        *(.bss)
    

    /DISCARD/ :
    
        *(.fini_array*)
        *(.comment)
    

我的开发环境是安装了 virtualbox 的 Linux Mint 18.1 64 位。我的代码和系列导师的代码差不多,但还是无法在虚拟机中启动。

编辑

我尝试使用qemu-system-i386 -kernel mykernel.bin,它与消息Hello World 一起工作正常。这意味着VirtualBox 环境和配置存在一些问题。

【问题讨论】:

在虚拟机启动过程中,您是否按 F12 并告诉它您希望从 CD-ROM 启动? 我怀疑您的问题可能与堆栈有关。您创建了一个非常大的 2mb 堆栈。因为它位于 BSS 部分,所以多重引导加载程序会将其归零。在某些环境中的问题是,如此大的 BSS 段可能会导致 multboot 加载程序在加载和处理内核时失败。如果堆栈是问题,请尝试将大小设置为 2k(2048 字节)而不是 2mb。它有效吗? (即改成:.section .bss.space 2*1024 您应该尝试使用qemu 来查看您的内核是否正常。可能首先检查二进制 (.bin) ,然后检查 .iso。 @rakib_ :我尝试使用qemu-system-i386 -kernel mykernel.bin,它与消息Hello World 一起工作正常。这意味着VirtualBox 环境和配置存在一些问题。 @MichaelPetch :我将堆栈大小减小到 2KB 并且它起作用了!!!所以,在这里我们有一些很好的学习。您能否将其发布为带有一些好的链接(如果有)的答案,以进一步阅读。谢谢。 【参考方案1】:

我遇到了同样的问题。只需安装 grub-pc-bin 并重新编译内核即可在虚拟机上成功启动。

sudo apt-get install grub-pc-bin

另外,我不必更改 BSS 段大小。

【讨论】:

【参考方案2】:

我没有这个答案的官方来源。它实际上是基于我在 *** 上看到的经验和其他问题以及我所做的一些发现。

如果您在 BSS 段中创建大型内核引导堆栈,则会导致 GRUB 在某些环境中崩溃,而在其他环境中则不会。当 BSS 段的总大小似乎达到大约 2mb 时,通常会发生这种情况。 Virtualbox 似乎是出现问题的一个特例。 Virtualbox 中的问题似乎因版本和所使用的虚拟硬件配置而异。

您在loader.s 中创建的用于引导您的C++ 环境的堆栈不需要那么大。一旦你的内存管理和分配器到位,你就可以为更大的内核堆栈保留区域,并在那时将 SS:ESP 设置为它。

为此你应该考虑改变:

.section .bss
.space 2*1024*1024 #2MB  for stack to grow towards left side
kernel_stack:

到 1mb 或更小的东西。我可能会选择 64kb 之类的:

.section .bss
.space 64*1024 #64KB  for stack to grow towards left side
kernel_stack:

【讨论】:

我在 1 台机器上尝试了 2KB 并且它可以工作,而在具有相同操作系统的另一台机器上相同大小的崩溃(虽然 64 KB 对这台机器有效)。可能是 VirtualBox 或其他东西的差异。 你能否分享一些关于内核引导、BSS、堆栈等介绍的好教程(如果你知道的话)。我想探索更多。谢谢。 @InsaneCoder OS Dev Wiki 可能是你最好的选择

以上是关于无法在 VirtualBox 中启动自定义内核:“无法从启动介质中读取”的主要内容,如果未能解决你的问题,请参考以下文章

启用复制粘贴[关闭]时无法连接到VirtualBox内核服务

为 HP 瘦客户端编译自定义内核并使用 grub 创建可引导 ISO

Genymotion模拟器 virtual box无法启动,在virtualbox中启动提示下述物理网卡未找到

为自定义内核制作可启动的随身碟

Kali linux 2016无法打开virtualbox问题解决

Docker 快速启动终端无法在 Windows 10 中启动 VirtualBox VM