0

我正在学习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

如果我们更改为 directcall 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”

4

0 回答 0