【问题标题】:I can't seem to get this 16-bit memory detection assembly code to work我似乎无法让这个 16 位内存检测汇编代码工作
【发布时间】:2021-03-27 15:16:24
【问题描述】:

不久前,我正在研究一个简单的引导加载程序项目,我决定重新开始研究它。无论如何,我正在尝试使用BIOS INT=15H EAX=E820h 检测内存。我在循环中使用了中断,并为内存映射分配了空间来保存所有条目。现在我正在尝试从最后一个条目开始解析条目。我的目标是找到最大的 1MB 区域,可以用来存放我从磁盘读取的文件。

到目前为止,这是我的代码。它正在 Bochs 2.6.11 上进行测试,具有 32MB 的 RAM,其他一切都设置为默认设置。当然是16位实模式代码。

[bits 16]

BlEntry:
    
    [...] ; above here all I did was set all segments to 0, except DS which is set to 7C0h
    
    ;
    ; Build the system's memory map
    ;

    xor ebx, ebx ; HANDLE for next call
    mov edx, 534D4150h ; magic
    mov ecx, 20 ; buffer size
    sub sp, cx ; allocate 20 bytes
    mov di, sp ; di points to buffer
BlpBuildMmBegin:
    mov eax, 0E820h ; magic
    mov [di+20], DWORD 1 ; Request ACPI compat-entry
    int 15h
    jc BlInt10Failure
    cmp eax, edx ; magics should match
    jne BlInt10Failure
    test ebx, ebx ; are we finished?
    je BlpFindEiArea ; jump out
    sub sp, cx ; allocate 20 bytes again
    mov di, sp ; setup di again
    jmp BlpBuildMmBegin ; do it all again
BlpBuildMmEnd:
     
    ;
    ; Load the EI into memory
    ;
    
    ; try to find the highest address we can map the EI to
BlpFindEiAreaStart:
    add di, 20 ; move onto the next entry
    cmp di, bp
    je BlNoMemoryFailure
BlpFindEiArea:
    cmp [di+16], DWORD 1 ; check if we can use this memory region
    jne BlpFindEiAreaStart ; of not, try again
    cmp [di+8], DWORD 100000h ; 1MB EI file size
    jge BlpFindEiAreaEnd
    jmp BlpFindEiAreaStart
BlpFindEiAreaEnd:
    mov eax, DWORD [di]
    cli
    hlt ;hang the system for now, I still need to add more functionality here

正如代码所示,当我遍历所有内容时,执行跳转到BlNoMemoryFailure,它只是使用电传输出显示No Memory! 输出然后挂起系统。这就是问题所在 - 我无法让此代码停止说出该消息。我的结构错了吗?我在编写代码时引用了这个网站http://www.uruk.org/orig-grub/mem64mb.html

【问题讨论】:

  • 使用调试器检查条目是否有意义。此外,您似乎只检查了最后一批区域。你的循环错了。
  • BlNoMemoryFailure 会发生什么?您可以将其定义添加到您的问题中吗?你提到它应该输出东西然后挂起,但实际行为是不同的。

标签: assembly x86-16 bootloader boot


【解决方案1】:

最初int 0x15, eax=0xE820 返回一个 20 字节的结构。一个 ACPI 版本(我认为它是 ACPI 3.0 但没有检查并且可能是错误的)将其扩展为 24 字节,该版本在结构中引入了一个新的“标志”字段。

此代码在堆栈上为 20 字节结构分配空间(没有额外的标志字段):

    mov ecx, 20 ; buffer size
    sub sp, cx ; allocate 20 bytes

mov [di+20], DWORD 1 ; Request ACPI compat-entry 预先设置了不在原始 20 字节中的额外标志字段,并破坏了堆栈,因为只分配了 20 字节。

int 15h 导致cx(最大缓冲区大小)中的值被替换为“实际返回的数据大小”。

循环结束后的sub sp, cx 释放了int 0x15 返回的许多字节数据,这可能与实际分配的原始最大缓冲区大小完全不同;可能会再次损坏堆栈(特别是如果您尝试通过将mov ecx, 20 替换为mov ecx, 24 来解决以前的问题)。

另请注意,BIOS 可以通过两种不同的方式处理“到达列表末尾”;并且为列表中的最后一个条目返回ebx = 0 只是一种可能性。另一种可能性是 BIOS 在ebx 中为最后一个条目返回一个非零值,然后当您尝试使用该值获取最后一个条目之后的条目时返回错误。出于这个原因,你不能只做jc BlInt10Failure

用于可靠地检测“到达循环结束”;我建议做一个初始的int 0x15 来获取第一个执行jc BlInt10Failure 的条目,然后是一个循环来获取所有剩余的执行jc BlpFindEiArea 的条目(换句话说,第一个条目之后的“失败”是被视为“列表末尾”而不被视为失败)。

请注意,如果您确实使用初始 int 0x15 来获取第一个条目,那么这也可以确定您使用的是返回 20 字节结构还是 24 字节(或更大)结构的 BIOS;这意味着您可以有 2 个单独的循环,其中一个不需要预先设置额外的标志字段(因为它知道它没有被使用),另一个不打扰预先设置额外的标志字段(因为它知道它会设置)。如果您的代码“非常具有防御性”并检查返回的数据是否正常(例如,“类型”字段不是不可能的值),它也很有用;和/或如果您想跟踪内存映射的来源(例如,使用某种 enum 表示“较新的 0xE820”、“较旧的 0xE820”,或在 BIOS 上使用的大约 8 个其他较旧的替代方案中的任何一个, 或 UEFI)。

一旦你有了一个条目列表,你可能不应该盲目相信它。在排序列表时检查“类型”字段中的未知值(并将它们替换为单个“未知/保留”值)是一个好主意(因此它不是随机顺序),同时检测是否报告了任何区域重叠(并通过找到用于每种可能性的最危险的替代“类型”来处理“重叠但报告为不同类型”情况的代码),同时丢弃任何具有“大小= 0字节”的条目(这可能发生 - 例如BIOS 使用静态定义的条目号和..)。请注意,不同的计算机有不同的错误,在某些情况下,int 0x15 会被其他东西“钩住”(例如,PXE/网络引导 ROM 通常将int 0x15 重定向到它自己的代码以隐藏 ROM 本身正在使用的内存) .

我也不相信int 0x15, eax=0xE820 不会修改各种值。例如,我不会假设它不会修改 ebpedx 中的值(即使它不应该),或者(如果缓冲区大小更大)它不会覆盖该值在 [es:di+20] 但仍然只返回 20 个字节(即使它不应该),或者它不会返回 carry = clear, ah = error code because the function failed(即使它不应该)。

最后;当您搜索生成的(经过良好排序和完整性检查的)内存映射时; “区域地址”和“区域大小”字段是 64 位的,因此您不能只比较最低的 32 位(对于有符号数字,您不应该使用 jge - 对无符号数字使用 jae )。换句话说,这是:

 cmp [di+8], DWORD 100000h 
 jge BlpFindEiAreaEnd

..应该是:

 cmp dword [di+8+4], 0          ;Is size >= 1 MiB?
 ja BlpFindEiAreaEnd            ; yes
 cmp dword [di+8], 0x00100000   ;Is size >= 1 MiB?
 jae BlpFindEiAreaEnd           ; yes
                                ; no

..但是当您说您正在寻找最高可用地址的 RAM(并怀疑这是另一个错误)时,我不知道您为什么要检查“大小 >= 1 MiB”。

【讨论】:

  • 我是个白痴,没有意识到mov [di+20], DWORD 1 对堆栈做了什么。很棒的详细解释,这对我有很大帮助。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-14
  • 2020-12-11
  • 1970-01-01
相关资源
最近更新 更多