无法在 VirtualBox 中启动自定义内核:"could not read from boot medium"
Unable to boot a custom kernel in VirtualBox : "could not read from boot medium"
我按照 Write your own operating system in 1 hour 上的教程系列创建了一个基本的 OS 来仅使用 4 个文件打印“Hello World”:Makefile
、kernel.cpp
、loader.s
和 linker.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] != '[=12=]';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)
}
}
我的开发环境是LinuxMint 18.1 64位,安装了virtualbox。我的代码和系列中导师的代码几乎一致,但我仍然无法在虚拟机中启动。
编辑
我试过 qemu-system-i386 -kernel mykernel.bin
,它在消息 Hello World
下工作正常。这意味着 VirtualBox 环境和配置存在一些问题。
我没有这个答案的官方来源。它实际上是基于我在 Whosebug 上看到的经验和其他问题以及我所做的一些发现。
如果您在 BSS 段中创建大型内核 bootstrap 堆栈,它会导致 GRUB 在某些环境中崩溃而不是其他人。当 BSS 段的总大小似乎达到约 2mb 时,通常会发生这种情况。 Virtualbox 似乎是出现问题的特例。 Virtualbox 中的问题似乎因版本和所使用的虚拟硬件配置而异。
您在 loader.s
到 bootstrap 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:
我运行遇到了同样的问题。只需安装 grub-pc-bin 并重新编译内核即可使其在虚拟机上成功启动。
sudo apt-get install grub-pc-bin
此外,我不必更改 BSS 段大小。
我按照 Write your own operating system in 1 hour 上的教程系列创建了一个基本的 OS 来仅使用 4 个文件打印“Hello World”:Makefile
、kernel.cpp
、loader.s
和 linker.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] != '[=12=]';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)
}
}
我的开发环境是LinuxMint 18.1 64位,安装了virtualbox。我的代码和系列中导师的代码几乎一致,但我仍然无法在虚拟机中启动。
编辑
我试过 qemu-system-i386 -kernel mykernel.bin
,它在消息 Hello World
下工作正常。这意味着 VirtualBox 环境和配置存在一些问题。
我没有这个答案的官方来源。它实际上是基于我在 Whosebug 上看到的经验和其他问题以及我所做的一些发现。
如果您在 BSS 段中创建大型内核 bootstrap 堆栈,它会导致 GRUB 在某些环境中崩溃而不是其他人。当 BSS 段的总大小似乎达到约 2mb 时,通常会发生这种情况。 Virtualbox 似乎是出现问题的特例。 Virtualbox 中的问题似乎因版本和所使用的虚拟硬件配置而异。
您在 loader.s
到 bootstrap 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:
我运行遇到了同样的问题。只需安装 grub-pc-bin 并重新编译内核即可使其在虚拟机上成功启动。
sudo apt-get install grub-pc-bin
此外,我不必更改 BSS 段大小。