Int 10H 在 QEMU 中不工作
Int 10H not working in QEMU
我正在学习 x86 实模式编程,并使用 QEMU 编写了一个小型引导加载程序来测试它。我选择了 GNU assembler 来学习。
这里是汇编代码:
#
# boot.s
#
.section .text
.globl start
start:
//setup stack
mov [=10=]x7c0, %ax
mov %ax, %ss
mov 2, %sp
//setup video
mov [=10=]x0, %eax
mov [=10=]x0, %al
int [=10=]x10
//print a character say 'm'
mov $'m', %al
mov [=10=]x0E, %ah
int [=10=]x10
1:
jmp 1b
以下文本显示在 QEMU 显示屏上:
Booting from Hard disk...
问题: 上面的信息打印出来了,还是像什么都不做一样。
我用来assemble、link的脚本是:
> to assemble : gcc -c boot.s
> to link : ld -T link.ld boot.o -o b.bin
> to put on bootsector of Hard-disk image
dd if=b.bin of=HD.img conv=notrunc
> to attach boot magic
echo -ne "\x55\xaa" | dd seek=510 bs=1 of=HD.img
> to emulate: qemu-system-i386 HD.img
从某处看到的在线教程复制的 linker 脚本,因为我自己不知道如何创建一个:
OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .;
}
我是否需要指定任何额外的参数或代码中存在一些错误?我以为是堆栈的设置,但尝试了很多可能性,但没有用。
如何绕过硬盘启动消息并让我的引导加载程序在屏幕上显示字母 m
?
我的工作平台是Fedora 23。
您的 linker 脚本似乎是为保护模式内核加载到 0x00100000 的环境编写的。这对于符合 multiboot specification 的引导加载程序来说很常见。像你这样的实模式引导加载程序将从磁盘的前 512 个字节读取并放置在内存中的 0x7c00 处。您将需要一个使用原点 0x7c00 的 linker 脚本。也可以使用linker脚本输出启动盘签名0xAA55.
一个基本的引导加载程序 linker 脚本可能看起来像这样 link.ld
:
OUTPUT_FORMAT("binary");
ENTRY(start);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00) {
*(.text);
}
.data : SUBALIGN(0) {
*(.data);
*(.rodata);
}
.bss : SUBALIGN(4) {
__bss_start = .;
*(COMMON);
*(.bss)
. = ALIGN(4);
__bss_end = .;
}
__bss_sizel = SIZEOF(.bss)>>2;
__bss_sizeb = SIZEOF(.bss);
/* Boot signature */
.sig : AT(0x7DFE) {
SHORT(0xaa55);
}
}
您的 boot.s
没有被告知生成 16 位代码。您可以将指令 .code16
放在汇编文件的顶部,以强制它生成 16 位指令。您还必须修改 GCC 命令以编译为 16 位代码,link 用户将需要 -melf_i386
选项。
您的引导加载程序代码应该真正设置 DS 寄存器以及堆栈。由于 linker 脚本假定代码位于内存位置 0x07c00,因此我们需要将 DS 设置为零。在 segmented model 中,物理地址 = (segment<<4)+offset 。 DS 设置为 0,0x7c00 的原点(基本偏移量)将映射到 (0<<4)+0x7c00 = 物理地址 0x07c00,这是我们的引导加载程序由 BIOS 加载的位置。
我用一些写了一个答案,可能有一定的价值。
清理后 boot.s
可能看起来像:
#
# boot.s
#
.code16
.section .text
.globl start
start:
//setup stack
mov [=11=]x7c0, %ax
mov %ax, %ss
mov 2, %sp
xor %ax, %ax # AX = 0
mov %ax, %ds # Set DS = 0 since origin point is 0x7c00
//setup video
xor %ax, %ax # Zero 16-bit AX register (includes AL and AH)
//mov [=11=]x0, %ax # Works but is not preferred for zeroing a reg
int [=11=]x10
//print a character say 'm'
mov $'m', %al
mov [=11=]x0E, %ah
int [=11=]x10
1:
jmp 1b
至assemble、link并生成您可以使用的磁盘映像:
gcc -c boot.s -m16
ld -melf_i386 -T link.ld boot.o -o b.bin -nostdlib --nmagic
dd if=b.bin of=HD.img conv=notrunc
然后您可以 运行 将图像用作软盘:
qemu-system-i386 -fda HD.img
或作为硬盘映像使用:
qemu-system-i386 -hda HD.img
我正在学习 x86 实模式编程,并使用 QEMU 编写了一个小型引导加载程序来测试它。我选择了 GNU assembler 来学习。
这里是汇编代码:
#
# boot.s
#
.section .text
.globl start
start:
//setup stack
mov [=10=]x7c0, %ax
mov %ax, %ss
mov 2, %sp
//setup video
mov [=10=]x0, %eax
mov [=10=]x0, %al
int [=10=]x10
//print a character say 'm'
mov $'m', %al
mov [=10=]x0E, %ah
int [=10=]x10
1:
jmp 1b
以下文本显示在 QEMU 显示屏上:
Booting from Hard disk...
问题: 上面的信息打印出来了,还是像什么都不做一样。
我用来assemble、link的脚本是:
> to assemble : gcc -c boot.s
> to link : ld -T link.ld boot.o -o b.bin
> to put on bootsector of Hard-disk image
dd if=b.bin of=HD.img conv=notrunc
> to attach boot magic
echo -ne "\x55\xaa" | dd seek=510 bs=1 of=HD.img
> to emulate: qemu-system-i386 HD.img
从某处看到的在线教程复制的 linker 脚本,因为我自己不知道如何创建一个:
OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .;
}
我是否需要指定任何额外的参数或代码中存在一些错误?我以为是堆栈的设置,但尝试了很多可能性,但没有用。
如何绕过硬盘启动消息并让我的引导加载程序在屏幕上显示字母 m
?
我的工作平台是Fedora 23。
您的 linker 脚本似乎是为保护模式内核加载到 0x00100000 的环境编写的。这对于符合 multiboot specification 的引导加载程序来说很常见。像你这样的实模式引导加载程序将从磁盘的前 512 个字节读取并放置在内存中的 0x7c00 处。您将需要一个使用原点 0x7c00 的 linker 脚本。也可以使用linker脚本输出启动盘签名0xAA55.
一个基本的引导加载程序 linker 脚本可能看起来像这样 link.ld
:
OUTPUT_FORMAT("binary");
ENTRY(start);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00) {
*(.text);
}
.data : SUBALIGN(0) {
*(.data);
*(.rodata);
}
.bss : SUBALIGN(4) {
__bss_start = .;
*(COMMON);
*(.bss)
. = ALIGN(4);
__bss_end = .;
}
__bss_sizel = SIZEOF(.bss)>>2;
__bss_sizeb = SIZEOF(.bss);
/* Boot signature */
.sig : AT(0x7DFE) {
SHORT(0xaa55);
}
}
您的 boot.s
没有被告知生成 16 位代码。您可以将指令 .code16
放在汇编文件的顶部,以强制它生成 16 位指令。您还必须修改 GCC 命令以编译为 16 位代码,link 用户将需要 -melf_i386
选项。
您的引导加载程序代码应该真正设置 DS 寄存器以及堆栈。由于 linker 脚本假定代码位于内存位置 0x07c00,因此我们需要将 DS 设置为零。在 segmented model 中,物理地址 = (segment<<4)+offset 。 DS 设置为 0,0x7c00 的原点(基本偏移量)将映射到 (0<<4)+0x7c00 = 物理地址 0x07c00,这是我们的引导加载程序由 BIOS 加载的位置。
我用一些
清理后 boot.s
可能看起来像:
#
# boot.s
#
.code16
.section .text
.globl start
start:
//setup stack
mov [=11=]x7c0, %ax
mov %ax, %ss
mov 2, %sp
xor %ax, %ax # AX = 0
mov %ax, %ds # Set DS = 0 since origin point is 0x7c00
//setup video
xor %ax, %ax # Zero 16-bit AX register (includes AL and AH)
//mov [=11=]x0, %ax # Works but is not preferred for zeroing a reg
int [=11=]x10
//print a character say 'm'
mov $'m', %al
mov [=11=]x0E, %ah
int [=11=]x10
1:
jmp 1b
至assemble、link并生成您可以使用的磁盘映像:
gcc -c boot.s -m16
ld -melf_i386 -T link.ld boot.o -o b.bin -nostdlib --nmagic
dd if=b.bin of=HD.img conv=notrunc
然后您可以 运行 将图像用作软盘:
qemu-system-i386 -fda HD.img
或作为硬盘映像使用:
qemu-system-i386 -hda HD.img