env_run:1. 進入user mode
先call env_pop_tf(&e->env_tf)
把關於此environment的一些register直從trapframe讀出來放到register裡面, 然後呼叫iret
__asm __volatile("movl %0,%%esp\n"
"\tpopal\n"
"\tpopl %%es\n"
"\tpopl %%ds\n"
"\taddl $0x8,%%esp\n" /* skip tf_trapno and tf_errcode */
"\tiret" /* goto eip, set eip to elf->entry */
: : "g" (tf) : "memory");
iret之後就進入user mode, 此時已經將eip指到elf->entry, 也就是此程式的開頭位置.
2. Load_icode
在load_icode中必須要根據現在的binary (ELF) 格式來load program
一個elf裡面會有多個program header, 每個代表一個segment.
Elf裡面有e_entry的virtual address, 用來代表process的起始位置.
"This member gives the virtual address to which the system first transfers control, thus starting the process."
每個environment有個trapframe, 用來儲存register當此environment沒有在執行的時候. (所以當要執行此env時要去load這些存在trapframe的registers)
"holds the saved register values for the environment while that environment is not running: i.e., when the kernel or a different environment is running. The kernel saves these when switching from user to kernel mode, so that the environment can later be resumed where it left off."
Trapframe的第一個member是tf_regs, 儲存trapframe的register
load_icode的最後必須要將env_tf.tf_eip 設定成 elf-> e_entry
3. IDT的設定: idt_init
- 先將kernel的TSS設定到GDT裡面, 之後當int發生時必須使用
- load the TSS: ltr(GD_TSS)
- 設定idt
SETGATE(idt[0x0], 1, GD_KT, do_divide_error, 0);
SETGATE(idt[0xd], 1, GD_KT, do_general_protection, 0);
SETGATE(idt[0xe], 1, GD_KT, do_page_fault, 0);
SETGATE(idt[0x28], 1, GD_KT, do_badsegment, 0);
裡面的interrupt handler是用aseembly定義的, 在trapentry.S
TRAPHANDLER_NOEC(do_divide_error, 0x0)
_alltraps:
pushl %ds;
pushl %es;
pushal;
movl $GD_KD, %eax;
movl %eax, %ds;
movl %eax, %es;
movl %esp, %eax;
pushl %eax;
call trap;
Reference:
1. ELF header: http://www.sco.com/developers/gabi/1998-04-29/ch4.eheader.html
2. Program header: http://www.sco.com/developers/gabi/latest/ch5.pheader.html
沒有留言:
張貼留言