我正在开发一个玩具内核,用于娱乐和教育(不是课堂项目)。我正在开始处理我的内存管理器,因此我仍然在实时模式下使用INT 0x15, EAX=E820
调用从BIOS获取内存映射。我正在改编我的函数来自osdev wiki(here,在“获取E820内存映射”一节中。但是,我希望这是一个我可以从我的C代码调用的函数,所以我试图改变它。我希望它有两个参数:一个指向存储映射条目的位置的指针,以及一个指向整数的指针,该整数将以表中的条目数递增。
根据wiki,ES:DI
需要指向应该存储数据的位置,因此我将我的第一个参数拆分为两个(段选择器pointer_to_map / 16
和偏移量{{ 1}})。这是C代码的一部分:
pointer_to_map % 16
这是我的ASM代码的一部分:
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();
这就是我所有的粘贴因为程序三重故障并关闭了那里的VM。通过移动; 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
命令,我发现函数在第一行崩溃了。如果我在C中注释掉这个电话,那么一切都会按照您的预期运作。
我已经通过Google阅读了几乎没有理由直接设置ret
,并且在我发现的代码中,他们将其设置为文字。我应该如何设置ES:DI
,如果我不应该直接设置它,我应该如何使C和ASM以正确的方式进行交互?
答案 0 :(得分:2)
每个段寄存器(在80x86上)都有一个可见部分和几个隐藏字段(段基,段限制和段的属性 - 读/写,权限级别等)。
在保护模式下;当您加载段寄存器时,CPU使用可见部分作为GDT或LDT的索引,并从该描述符(在GDT或LDT中)加载段的隐藏字段。
在实模式下; CPU完全不同 - 它只将段基设置为“可见部分* 16”,不使用任何(GDT,LDT)表。
鉴于您使用的是指向数据表的32位指针和32位堆栈指针(例如mov es, [esp + 4]
);我假设您的C代码处于32位保护模式。这与实模式完全不兼容,部分原因是段加载完全不同,部分原因是默认操作数/地址大小为32位而不是16位。
所有BIOS功能均专为实模式设计。它们不能用于保护模式。
基本上;我建议:
英特尔系统程序员指南中包含从实模式切换到保护模式以及从保护模式切换到实模式的说明。 :)