为什么启动 AP 需要在 mit6.828 示例操作系统内核中进行间接调用?

问题描述

我正在学习mit6.828课程,我不明白为什么在lab4中启动AP时必须间接调用。我尝试直接调用call mp_main(源代码movl $mp_main,%eax; call *%eax),但它导致了三重故障。这是部分源代码

###################################################################
# entry point for APs
###################################################################

# Each non-boot cpu ("AP") is started up in response to a STARTUP
# IPI from the boot cpu.  Section B.4.2 of the Multi-Processor
# Specification says that the AP will start in real mode with CS:IP
# set to XY00:0000,where XY is an 8-bit value sent with the
# STARTUP. Thus this code must start at a 4096-byte boundary.
#
# Because this code sets DS to zero,it must run from an address in
# the low 2^16 bytes of physical memory.
#
# boot_aps() (in init.c) copies this code to MPENTRY_PADDR (which
# satisfies the above restrictions).  Then,for each AP,it stores the
# address of the pre-allocated per-core stack in mpentry_kstack,sends
# the STARTUP IPI,and waits for this code to ackNowledge that it has
# started (which happens in mp_main in init.c).
#
# This code is similar to boot/boot.S except that
#    - it does not need to enable A20
#    - it uses MPBOOTPHYS to calculate absolute addresses of its
#      symbols,rather than relying on the linker to fill them


    # Switch to the per-cpu stack allocated in boot_aps()
    movl    mpentry_kstack,%esp
    movl    $0x0,%ebp       # nuke frame pointer

    # Call mp_main().  (Exercise for the reader: why the indirect call?)
    movl    $mp_main,%eax
    call    *%eax

我不知道如何在多 cpu 中使用 gdb 进行调试,尤其是在启动时,但 entry.S 与此代码类似,并且很容易调试。这里是 entry.S 密钥代码

    mov $relocated,%eax
    jmp *%eax   
relocated:
    movl    $0x0,%ebp           # nuke frame pointer

下面是正确代码的gdb输出。在 jmp *%eax 之前,eip 是低地址,但之后 eip 是高地址。下一条指令是mov $0,%ebp

(gdb) si
=> 0x10002d:    jmp    *%eax
0x0010002d in ?? ()
(gdb) info reg eax eip
eax            0xf010002f   -267386833
eip            0x10002d 0x10002d
(gdb) si
=> 0xf010002f:  mov    $0x0,%ebp
0xf010002f in ?? ()
(gdb) info reg eax eip
eax            0xf010002f   -267386833
eip            0xf010002f   0xf010002f

如果我们改成直接call relocated而不是mov $relocated,%eax; jmp *%eax,会导致三重故障,gdb输出

(gdb) 
=> 0x100028:    call   0x10002d
0x00100028 in ?? ()
(gdb) info reg eip 
eip            0x100028 0x100028
(gdb) si
=> 0x100028:    call   0x10002d
0x00100028 in ?? ()

如果我们查看 objdump 输出

    call relocated
f0100028:   e8 00 00 00 00          call   f010002d <relocated>

f010002d <relocated>:
relocated:

    # Clear the frame pointer register (EBP)
    # so that once we get into debugging C code,# stack backtraces will be terminated properly.
    movl    $0x0,%ebp           # nuke frame pointer
f010002d:   bd 00 00 00 00          mov    $0x0,%ebp

这里虚拟地址 [0xf0000000,0xf0000000+4MB) 和 [0,4MB) 都映射到物理地址 [0,4MB)

我想知道间接调用如何改变这一点。您可以在 github 中找到的完整代码只需在 kern/entry.S 和 kern/mpentry.S 中搜索“mit6.828”

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)