MINIX 3中的代码将启动监视器(引导程序)GDT复制到内核空间并切换它。但我很难理解代码。在代码中,_gdt
是在C(gdt[GDT_SIZE]
)中声明的描述符表数组的地址。
结构gdt如下:
struct segdesc_s { /* segment descriptor for protected mode */
u16_t limit_low;
u16_t base_low;
u8_t base_middle;
u8_t access; /* |P|DL|1|X|E|R|A| */
u8_t granularity; /* |G|X|0|A|LIMT| */
u8_t base_high;
};
结构的大小是8个字节。宏GDT_SELECTOR
的值为8
。
! Copy the monitor global descriptor table to the address space of kernel and
! switch over to it. Prot_init() can then update it with immediate effect.
sgdt (_gdt+GDT_SELECTOR) ! get the monitor gdtr
mov esi, (_gdt+GDT_SELECTOR+2) ! absolute address of GDT
mov ebx, _gdt ! address of kernel GDT
mov ecx, 8*8 ! copying eight descriptors
copygdt:
eseg movb al, (esi)
movb (ebx), al
inc esi
inc ebx
loop copygdt
最混乱的一行是movb (ebx), al
。请帮忙。
答案 0 :(得分:3)
这是一种奇怪的asm语法。它正在使用()
作为AT& T语法等内存操作数,但只有在英特尔语法的左侧目标才有意义。 (它还使用AT& T风格的助记符后缀作为操作数大小,如movb
用于字节mov
。)
我认为它基本上是NASM语法,但使用()
代替[]
,因为评论说mov ebx, _gdt
是地址的mov-immediate。在GAS .intel_syntax noprefix
中,这将是MASM语法中的加载。
Minix的编译器有自己的asm风格,它是documented here。(感谢@MichaelPetch)。
所以这是一个一次一个字节的复制循环,从es:esi
到ds:edi
,用于ecx=8*8
个字节。这正是评论说它确实如此,这样可以很容易地找出我之前从未见过的语法。
movb (ebx), al
将AL存储到内存中,位于EBX中的地址。即NASM mov [ebx], al
或AT& T mov %al, (%ebx)
。
商店正在使用EBX的默认段选择器,即DS。您通常不需要在32位模式下提及段,但请注意加载上的eseg
前缀。您尚未展示,评论未提及,ES设置的内容,以及与DS不同的原因/方式。
代码似乎针对代码大小进行了优化,而不是速度(这很好,因为它只在启动时运行一次)。例如它使用慢loop
指令,并且一次复制一个字节,因此它可以inc
指针(1个字节)而不是add esi, 4
(3个字节)。尽管如此,我怀疑使用索引寻址模式,你可以使它只是一样小,但一次复制4个字节。 (字节数固定为8*8
,因此它总是4的倍数。)
循环非常接近rep movsb
(or rep movsd
)所做的,即将ecx
元素从DS:(E)SI
复制到ES:(E)DI
。 (可以使用细分前缀覆盖ds
,因此您可以例如从fs:esi
复制到es:edi
)。但是在Minix代码中,加载来自ES:某些内容,而movs
始终使用es
作为目标细分。
fseg rep movsd
会比循环更紧凑(也更快),但可能会有适当设置段寄存器的障碍。使用EDI和ESI代替ESI和EBX不应成为障碍。