1

我正在开发一个用于娱乐和教育的玩具内核(不是课堂项目)。我开始在我的内存管理器上工作,所以我试图INT 0x15, EAX=E820在仍处于实模式时使用调用从 BIOS 获取内存映射。我正在从 osdev wiki 调整我的功能(这里,在“获取 E820 内存映射”部分)。但是,我希望这是一个可以从我的 C 代码中调用的函数,所以我正在尝试对其进行一些更改。我希望它接受两个参数:一个指向存储映射条目的位置的指针,以及一个指向整数的指针,该整数将按表中的条目数递增。

根据 wiki,ES:DI需要指向应该存储数据的位置,所以我将第一个参数分成两个(段选择器pointer_to_map / 16和偏移量pointer_to_map % 16)。这是C代码的一部分:

typedef struct SMAP_entry {
    unsigned int baseL; // Base address, a QWORD
    unsigned int baseH;
    unsigned int lengthL; // Length, a QWORD
    unsigned int lengthH;
    unsigned int type; // entry type
    unsigned int ACPI; // extra data from ACPI 3.0
} SMAP_entry_t;

SMAP_entry_t data[100];
kprint("Pointer: ");
kprint_int((int) data, 16);
kprint_newline();

int res = 0;
read_mem_map(((int) data) / 16, ((int) data) % 16, &res);
kprint("res: ");
kprint_int(res, 16);
kprint_newline();

这是我的 ASM 代码的一部分:

; performs a INT 0x15, eax=0xE820 call to find the memory map
; inputs: the pointer to the data table / 16, the pointer % 16, a pointer to an dword (int) which will be
;       incremented by the number of entries after this function returns.
; preserves: no registers except esi
read_mem_map:
    mov es, [esp + 4]           ; set es to the value of the first argument
    mov di, [esp + 8]           ; set di to the value of the second argument

这就是我要粘贴的全部内容,因为程序会出现三重故障并关闭那里的 VM。通过移动ret命令,我发现函数在第一行就崩溃了。如果我注释掉 C 中的调用,那么一切都会如您所愿。

我已经通过谷歌阅读了几乎没有理由ES:DI直接设置,并且在我发现的代码中,他们将其设置为文字。我应该如何设置ES:DI,如果我不应该直接设置,我应该如何让 C 和 ASM 以正确的方式交互?

4

1 回答 1

2

每个段寄存器(在 80x86 上)都有一个可见部分和几个隐藏字段(段基、段限制和段属性 - 读/写、特权级别等)。

在保护模式下;当您加载段寄存器时,CPU 使用可见部分作为 GDT 或 LDT 的索引,并从该描述符(在 GDT 或 LDT 中)加载段的隐藏字段。

在实模式下;CPU 做了一些完全不同的事情——它只将段基数设置为“可见部分 * 16”并且不使用任何(GDT、LDT)表。

鉴于您使用的是指向数据表的 32 位指针和 32 位堆栈指针(例如mov es, [esp + 4]);我假设您的 C 代码处于 32 位保护模式。这与实模式完全不兼容,部分原因是段加载的工作方式完全不同,部分原因是默认操作数/地址大小是 32 位而不是 16 位。

所有 BIOS 功能都是为实模式设计的。它们不能在保护模式下使用。

基本上; 我建议:

  • 将指向数据表的指针作为 32 位整数/指针(而不是 2 个单独的 16 位整数)传递给程序集
  • 调用“进入实模式”函数(这会有点棘手,因为您还将从 32 位堆栈切换到 16 位堆栈,并且需要 16 位中的“32 位返回指令”代码)。
  • 将指向数据表的指针拆分为其段和汇编中的偏移量,然后加载段(它应该可以正常工作,因为您现在处于实模式)
  • 调用 BIOS 函数(它应该可以正常工作,因为你现在处于实模式)
  • 调用“进入保护模式”函数(这将再次有点棘手,包括 32 位代码中的“16 位返回指令”)。
  • 返回到(32 位保护模式)调用者

从实模式切换到保护模式以及从保护模式切换到实模式的说明包含在英特尔的系统程序员指南中。:)

于 2014-12-21T18:19:56.697 回答