对x86保护模式内存布局感到困惑

对x86保护模式内存布局感到困惑,x86,bootloader,protected-mode,X86,Bootloader,Protected Mode,在尝试编写x86引导加载程序并在此过程中询问我的方法一段时间后,我认为我需要后退一步,澄清我对实际尝试实现的目标的一些误解: 我了解,在启动过程开始时,BIOS从启动介质读取第一个512字节,并将其加载到内存地址0x7C00处,低于0x500向下,高于0x80000向上(给予或接受)。有BIOS/视频特定的内容加载到内存中,引导加载程序不应接触这些内容 但我不确定当引导加载程序切换到保护模式时情况会发生什么变化。假设我在执行此操作之前不重新定位引导加载程序代码,我只是禁用中断,启用A20,然后使

在尝试编写x86引导加载程序并在此过程中询问我的方法一段时间后,我认为我需要后退一步,澄清我对实际尝试实现的目标的一些误解:

我了解,在启动过程开始时,BIOS从启动介质读取第一个512字节,并将其加载到内存地址0x7C00处,低于0x500向下,高于0x80000向上(给予或接受)。有BIOS/视频特定的内容加载到内存中,引导加载程序不应接触这些内容

但我不确定当引导加载程序切换到保护模式时情况会发生什么变化。假设我在执行此操作之前不重新定位引导加载程序代码,我只是禁用中断,启用A20,然后使用尽可能简单的GDT直接跳转到保护模式代码,即一个代码和一个数据段,两者都覆盖整个4GiB地址空间


完成之后,我假设引导加载程序代码仍然位于0x7C00。在将内核加载到内存之前,我可以将它移动到任何我想要的地方吗?即在0x0?那我的堆栈呢,我是不是让它从0xFFFFFF开始?当然,稍后我会想设置分页,但我不确定如何在到达该点之前布局我的内存。或者我的方法是次优的,我应该从一开始就设置不同的GDT?如果是这样的话,“典型”的做法是什么,将代码和数据段分离在一起,包含所有内存地址?或者甚至是一个单独的堆栈段?

更改模式时内存不会更改。您只需要一个“固定引用”,因为地址的计算方式随着模式的变化而变化。因为您使用的是平面映射(0-4GiB),所以只需将实模式逻辑地址转换为其线性形式(例如:1111:2222->13332)。堆栈必须位于实际存在RAM的地址,0xFFFFFFFF始终映射到闪存ROM,因此它不可写,并且所有推送都将被愚蠢地丢弃。通常的方法是包含所有地址空间的代码和数据段。如果创建原点为0x7c00的静态二进制文件,则必须将其加载到地址0x7c00处的内存中,除非找到某种写入位置无关代码的方法。如果要在0x20000处加载内核(例如),则必须在链接器脚本中将其设置为原点。不要将堆栈设置为奇数地址,也不能写入所有内存。0xFFFFFF可能不可写,并且包含BIOS的副本。通常最好的加载位置是从0x100000开始,因为它避免了前1MiB中的所有保留间隔,包括BIOS/视频Ram/EBDA等。@MargaretBloom我想我现在明白了,我不应该将任何内容映射到0xA0000-0x100000地址范围,也不能映射物理Ram末尾以上的任何内容。因此,在我看来,将内核加载到0x100000是最有意义的。@MichaelPetch但是0xA0000以下的地址实际上可以使用什么呢?如果我想切换回实模式,我想我必须保留实模式IVT和(E)BDA,对吗?因此,我是否可以将引导加载程序重新定位到EBDA的正下方,并将esp设置为引导加载程序代码的正下方?如果您所在的系统内存最小为1MB,则0x500到EBDA开始之间的所有空间都将被占用。EBDA刚好低于0xA0000,大小不一,但可以通过查看从地址0x00400开始的BDA找到位置(假设没有BIOS错误),特别是0x0040e的16位字具有EBDA的开始段。或者,0x00413处的16位字包含从0x00000处的内存开始到EBDA开始的可用千字节数。该值通常为639(639K),因为许多系统上的EBDA大小为1KB。