Bochs

来自osdev
Zhang3讨论 | 贡献2022年2月11日 (五) 09:58的版本
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳到导航 跳到搜索
Emulators
PC Emulators
PC Virtual Machine Monitors
PowerPC Emulators

Bochs是一个用于平台的模拟器,至今它还在稳步改进中。 它极大地改善了操作系统开发,因为虚拟机的重启速度比实际硬件快得多,此外,它还提供了详细的调试功能,可以在内核开发过程中提供很大帮助。

强烈建议安装两台Bochs- 除了开箱即用的版本(可以选择二进制程序包下载)之外,您还应该编译第二个启用了内部调试器的实例 - 因此,当您的 “正常” 调试工具无法跟踪的奇怪情况发生时,您可以将其与调试版本进行交叉检查。 您可能还希望启用(并使用)调试IO端口。


常见的错误消息

Running in Bogus Memory 在虚假内存中运行

您将代码指针(EIP)发送到某个未初始化的内存区域。 这意味着您要么引用了空(或未初始化)指针,要么损坏了栈帧Stack Frame的返回地址。 你可以尝试清理您的代码,在使用指针之前测试它们,初始化每个指针 (尤其是那些在堆栈上的指针) 并在GCC中启用 *-Wall*。

3rd exception with no resolution 未解决的第三异常

这是因为CPU没有设法调用异常处理程序,通常是triple fault|三重故障。 这可能是由于不良的 中断描述符表IDT 寄存器内容,或不良的IDT描述符导致的。 有时(但不太可能),这也可能是由于异常处理程序代码中的严重错误造成的。 检查异常是否与“非法”ASM指令(如idiv 0)一起工作,或者

 push 0xf001
 pop ds                   ; 0xf001 is no valid segment,
 mov ax, ~[ds:0x12345678] ; let's see if we get the GPF

在某些情况下,在此之前还有其他错误消息,可以在错误中提供更多详细信息。 可能会显示的一些常见消息:

  • interrupt(): gate descriptor is not valid sys seg
您尚未加载IDT,或者IDT已损坏
  • interrupt(): SS selector null
  • 您没有TSS
  • You haven't set SS0 / ESP0 in the TSS
  • CR0 = 0xe0000001 CR2 = 0xe0000001

:* 您的分页表没有对齐页面

* 您的页表未指向正确的内存部分

I/O操作数大小

Bochs对I/O操作数大小执行一些相当偏执的检查。 从端口0x1234读取一个字节(8位)通常与读取32位值不同。 你需要返回去查芯片data sheet,仔细检查大小是否正确。

fetch_raw_descriptor: LDTR.valid=0

许多人都说 “可是... 我没有LDT,我读到它不是强制性的!?“。 你是对的。 Bochs也没错。 这个消息通常意味着你的程序试图加载一个带有垃圾值的选择器,而这个垃圾值恰好设置了第三位(表指示符Table Indicator )。 CPU会尝试查找LDT中的描述符,但实际没有注册LDT! 在大多数情况下,错误来自堆栈上的push和pop的一些错误配对,这导致非选择器值被加载到段寄存器中。

如果你仍然卡住了,下载Bochs源代码包并搜索你收到的消息。 然后,也许您可以向消息打印代码添加额外的信息(如段错误的错误偏移量、段限制等)。 但是不要修改bochs的操作! 每次我怀疑Bochs中有漏洞,都只是我误解了英特尔手册...

Bochs和真实硬件之间的差异

Bochs启用BIOS中的A20线路
您的PC不一定会这样做。 有的PC有BIOS选项可以打开,有的没有。 检查启用A20线路的代码,确保其与更快的硬件没有问题。
Bochs会抹除它的内存
在Bochs中,在您(或BIOS)放入其他内容之前,内存始终为零。 在普通PC上,未初始化的内存往往包含垃圾(通常是全1)或以前执行的痕迹。 检查你的指针,初始化它们,必要时在纸上打印你的代码。
Bochs无法正确模拟CPU cache缓存/TLB

:尽管Bochs确实具有这些构造,但它们的工作方式与常规CPU缓存或TLB不同,并且不会根据正在仿真的CPU进行更改。 如果未正确处理缓存或TLB刷新,Bochs和硬件的行为可能会有所不同(即,它可能在Bochs上工作,但在硬件上不工作,反之亦然)。

Bochs软盘没有错误
在物理PC中,通常在扇区/磁道上发出最多3次读取尝试命令,然后才能进行良好读取。 如果您的引导扇区中没有正确的错误检查/恢复,您很可能会运行一些不是您的内核的东西……
Bochs对于返回实模式很灵活
不管Chris Giese的保护模式教程说明了什么,您不必处于16位保护模式才能清除CR0的PE位。 如果您未能在真实PC上进入16位保护模式,它将挂起,而不会给出任何错误指示-没有三重故障或任何东西!
Bochs上的计时不是真实耗时
(除非您将其配置得更接近实时)。 假如在bochs上等待2秒,打算让那些需要2秒才能准备好的虚拟设备准备就绪,但这可能对您来说只有0.02秒...或者可能是200秒。

附加

clock: sync=realtime, time0=local

将导致中断在实际时间间隔,但可能会让OS与PIT中断过载,导致堆栈溢出 (如果它是pre-emptible抢先的) 使用sync=slook也会将其设置为实时速度,只是每个虚拟秒之间的虚拟时钟周期是恒定的。 如果需要减速和模拟原始时钟,您需要去设置每秒执行的指令量。

CPU始终是Intel/AMD
Bochs模拟CPU,因此CPUID将始终报告为Intel(如果模拟32位系统)或AMD(如果模拟64位系统)报告,而不管系统中到底有什么。

较新版本的Bochs允许您在CPU标记中指定供应商字符串:

cpu: vendor_string="test        "
#必须是12个字符的ASCII字符串!

Bochs映像文件扼要

要使用Bochs引导您的自定义内核,请在bochsrc配置文件中搜索 “floppya” 和 “boot” 行。 以下配置允许您使用计算机的“真实”软盘驱动器并从中启动:

floppya: 1_44=/dev/fd0, status=inserted
boot: floppy

对于Windows用户,应显示为:

floppya: 1_44=a:, status=inserted
boot: floppy

但是这太慢了,当你处于一个紧凑的修改-构建-启动周期时,会给软盘带来很大的压力。 Bochs提供了映像文件的使用方法,包括创建映像文件的交互式工具 (bximage.exe)。 请注意,即使映像是设备而不是常规文件,Bochs也会模拟软盘的内部结构。。。

Bochs调试设施

Bochs具有一些简化调试的功能。其中许多必须通过配置开关启用:

GUI调试器

Bochs有一个命令行内部调试器,在此基础上还有一个图形界面。 您必须使用配置选项编译Bochs,然后编辑bochsrc文件才能启用GUI调试器。 我在bochsrc文件中使用这一行来启用X中的图形调试:

 display_library: x, options="gui_debug"

在Windows环境中,将此行添加到您的bochsrc.bxrc

 display_library: win32, options="gui_debug"

在Windows上,“option”标志(上面一行用来读取的内容)似乎会被接受,但GUI窗口不会出现。

I/O调试器宏

在启用I/O调试端口的情况下编译Bochs时,一些有用的宏(如果是Bochs 2.4或更新版本, port_e9_hack:enabled=1,如果没有可以试试 configure --port-e9-hack):

 //outputs a character to the debug console
 #define BochsConsolePrintChar(c) outportb(0xe9, c)
 //stops simulation and breaks into the debug console
 #define BochsBreak() outportw(0x8A00,0x8A00); outportw(0x8A00,0x08AE0);

Magic Breakpoint

当您将Bochs与 内部调试器 一起使用时,您可以通过名为 Magic Breakpoint 的工具触发调试器。 要触发断点,可以在代码中的任意位置插入 xchg bx, bx(在GAS语法中是,xchgw %bx, %bx),Bochs将在调试器执行它时立即捕获到调试器中。 在实际硬件上,这不起作用,因为它只是用自身替换BX寄存器。

您应该在Bochs配置文件中放入以下行,让它侦听magic breakpoint:

magic_break: enabled=1

在旧版本上,仅启用调试器无法在magic breakpoint支持下编译,在这些版本上配置构建时,还需要指定 lt;tt>--enable-magic-breakpoint

调试SMP

使用内部调试器时,可以使用以下命令切换CPU:

 set $cpu = <n>

我还发现这在GUI调试器中有时是必要的,即使它为每个CPU都有按钮。

内部调试器命令

您可以传递一个包含调试命令的文件,以便在使用内部调试器启动bochs时自动运行。 (bochs -rc <file>)

Bochs在BIOS加载之前放置了一个自动断点,可以通过将 continue 作为所述文件中的第一个命令来自动跳过该断点。

调试三重故障

使用内部调试器时,您可以在Bochs配置文件中更改此行:

reset_on_triple_fault 0

这一行在三重故障时禁用模拟器的重置,使您能够在三重故障发生后调试代码(在实现分页时非常有用)。

从源代码编译Bochs

Bochs有许多编译时配置选项,其中一些选项冲突,因此Bochs的二进制版本可能不适合您的目的。 我发现最好是编译自己的Bochs副本,以确保自己定制所需的功能。 此外,如果发布的版本较旧并且不适合您,您应该考虑使用Bochs的CVS快照版本。 例如我直到版本2.4发布都一直觉得这样做很有必要。 在Ubuntu上,你可能需要运行

  sudo apt-get install libgtk2.0-dev

并输入您的密码。在其他Linux发行版上,请尝试类型操作。

Bochs配置选项的清单可能会令人困惑,假如您不认为默认值是明智的。这些是我使用的选项,你可以考虑参考:

  ./configure --enable-smp \
              --enable-cpu-level=6 \
              --enable-all-optimizations \
              --enable-x86-64 \
              --enable-pci \
              --enable-vmx \
              --enable-debugger \
              --enable-disasm \
              --enable-debugger-gui \
              --enable-logging \
              --enable-fpu \
              --enable-3dnow \
              --enable-sb16=dummy \
              --enable-cdrom \
              --enable-x86-debugger \
              --enable-iodebug \
              --disable-plugins \
              --disable-docbook \
              --with-x --with-x11 --with-term --with-sdl2

几点注意事项

  • 如果您使用的是Windows,则最后一行可能应为“--with-Win32”。
  • 在Linux上,最好使用SDL作为X11的显示库,因为在某些设置下性能似乎会大大提高
  • Bochs支持GDB stub,并有自己的内部调试器。 这些不能编译成相同的Bochs二进制文件。 内部调试器非常有用,它的标志是 --enable-debugger
  • Bochs中的GDB stub不支持SMP,上次我检查过。
  • 如果不启用PCI,则Intel多处理表将不会出现在内存中。
  • 如果不指定--disable-plugins,就无法成功加载GUI调试器。 否则,就会出现动态加载符号错误。
  • 2.4.2版之后的几个特定于CPU的选项已合并到CPU级别规范中,因此已弃用。它们已从上面的示例中删除。
  • 默认编译不支持x86-64,-enable-x86-64选项会将其打开
  • 在许多Linux发行版上,可以通过软件包管理器安装Bochs。 例如,在使用apt-get的发行版上,我们可以这样做
  sudo apt-get install bochs
  sudo apt-get install bochs-x

要安装Bochs和X11插件(可能会在ubuntu/Linux mint上崩溃:安装SDL插件并使用SDL而不是x作为显示库)。 请注意,在包管理器的二进制文件中未启用图形调试器的可能性很大。

另见

文章

外部链接

de:Bochs