我刚开始使用我的第一个操作系统(我是这个领域的初学者)。我想在汇编和C中都这样做,但在汇编代码中调用C函数时遇到了麻烦。
** Boot .asm**:
BITS 32
global start
extern dmain
start:
call dmain
jmp $
times 510 - ($- $$) db 0
dw 0xAA55
main.c:
#define NULL 0
void clear();
void clear() // clear entire screen
{
char *mem = (char*)(0xb8000);
while (*mem != NULL){
*mem = NULL;
mem++;
}
}
void dmain(void *mbr, unsigned int magic) {
clear();
}
Makefile:
main:
gcc -m32 -ffreestanding -c main.c -o main.o
nasm -f win32 boot.asm -o boot.o
ld -m i386pe -T NUL -o main.tmp -Ttext 0x100000 boot.o main.o
objcopy -O binary main.tmp main.img
qemu-system-x86_64 main.img
这是我的输出:
gcc -m32 -ffreestanding -c main.c -o main.o
nasm -f win32 boot.asm -o boot.o
ld -m i386pe -T NUL -o main.tmp -Ttext 0x100000 boot.o main.o
boot.o:boot.asm:(.text+0x1): undefined reference to `dmain'
make: *** [Makefile:4: main] Error 1
1条答案
按热度按时间iovurdzv1#
如果你想写32位代码(如BITS 32所示),你必须设置一个保护模式环境。现在,CPU处于真实的模式,必须置于保护模式。
我看到您正在尝试将 Boot 扇区和引导加载程序可执行文件链接到一个文件中。使用适当的链接器脚本可以实现这一点,但是如果您仍然处于真实的模式并且不支持分段内存模型,则GCC无法修复16位地址,这意味着保护模式是必需的。您还生成了一个带有 Boot 扇区的PE可执行文件,这肯定不会起作用。BIOS只会加载512字节并跳转到它(顺便说一句,
DL
被设置到 Boot 盘)。更好的方法是将 Boot 加载程序构建为单独的二进制文件,并使用
INT 13h
从磁盘加载它。在32位保护模式环境中进入第二阶段,其中为平面模型设置了全局描述符表。第二阶段加载器可以是构建为在特定加载地址处运行的平面二进制。因为你所有的地址都将被链接器固定到你想加载第二阶段 Boot 加载程序的任何地方,所以当访问加载程序外部的内存(例如视频内存)时,有必要减去基址。
请注意,如果您决定进入受保护模式环境(在使用GCC时强烈建议),除非您设置虚拟8086模式和任务状态段,否则BIOS服务将不可用。
进入保护模式环境
BITS 32是一个汇编指令,实际上并不改变CPU的状态。必须设置CR 0的第一位才能进入,加载带有平面模型的GDT。
即使这样,您仍然只处于16位保护模式。需要跳转到标记为32位的代码段。
全局描述符表
每个条目的长度为8个字节。第一个是NULL段,必须归零。GDT可以放置在内存中的任何位置。要加载GDT,我们必须用LGDT设置GDTR。
以下示例显示了GDTR和GDT以及平面模型段之间的关系。
GDT的确切结构如下所示:https://i.stack.imgur.com/KNZeO.gif
GDT条目由选择器引用。位0和1是所请求的特权级别(出于输出目的为零),位2是GDT/LDT(此处也为GDT为零)。其余部分是表的13位索引。基本上,在这种情况下,通过简单地将索引移位三来引用段。
中断描述符表
如果要在保护模式下接收中断,IDT是必不可少的。它包含一个代码段选择器和一个32位地址。这可以在 Boot 加载程序环境中完成。
需要将PIC重新编程为适当的基向量,以便它们不会像在真实的模式中那样与x86异常冲突。
下面是8259 PIC(标准PC中断控制器)https://stanislavs.org/helppc/8259.html的硬件规格
C中的一个例子:https://wiki.osdev.org/8259_PIC#Code_Examples
虚拟8086模式
虚拟8086是一个复杂的主题,所以我将给予一些简短的细节,应该有助于避免陷阱。V86模式允许在保护模式下运行真实的模式代码。使用它需要一个至少带有#GP处理程序的IDT。
V86自动将用户的当前权限级别设置为3。任何特权指令或INT指令将导致一般保护故障,以便监视器可以模拟它。在#GP的堆栈上,保存的指令指针指向导致它的确切指令,因此请确保在模拟指令后递增它。
BIOS例程肯定有IO指令,因此请确保TSS有IO权限位图,并将所有位设置为零,以便允许所有端口。
TSS还具有称为ESP 0的字段,其是在进入环3时将被加载到ESP的栈指针值。必须在进入3号环或V86之前进行设置。用于进入V86的汇编例程可以将其设置为当前堆栈指针。
请参阅OSDev文章(我不久前曾对此做出贡献),以了解更多具体细节:https://wiki.osdev.org/Virtual_8086_Mode
进一步阅读和建议
这里有一个关于IA 32体系结构细节的优秀资源。这是英特尔i386的官方手册。
https://pdos.csail.mit.edu/6.828/2014/readings/i386/toc.htm
应关注以下主题: