- 论坛徽章:
- 0
|
在MINIX3内核被成功加载到内存后,控制权会转移到kernel/mpx.s文件,根据机器字长为16位或32位来分别加载mpx88.s或者mpx386.s文件并执行。
此时,cs和ds寄存器已经指向MINIX3内核的相应段,而ss寄存器仍指向boot的栈段,该栈中结构如下:(栈底)
aout结构体的地址
boot参数的大小和地址
boot的CS和IP(16位),以便MINIX3结束后将控制权交还给boot
(栈顶)
|
对于32位PC机,MINIX3内核会跳转到mpx386.s文件的MINIX标签处开始执行:MINIX:
jmp over_flags ! 跳过下面两个用于boot的数据
.data2 CLICK_SHIFT
flags:
.data2 0x01FD
nop
over_flags:
! 设置boot栈,以便MINIX3结束后交还控制权
movzx esp, sp ! 将boot栈扩展为32位
push ebp ! 将栈顶地址传送给ebp
mov ebp, esp
push esi
push edi
cmp 4(ebp), 0 ! 如果boot的cs和ip均为0则不可交还控制权
jz noret
inc (_mon_return)
noret: mov (_mon_sp), esp ! 保存ESP的值
! 将boot的GDT拷贝到内核地址空间,并加载
sgdt (_gdt+GDT_SELECTOR) ! 将GDT的描述结构拷贝到内存
! 低2字节为字节长,高4字节为基址
mov esi, (_gdt+GDT_SELECTOR+2) ! boot中GDT的基址
mov ebx, _gdt ! 内核中GDT的基址
mov ecx, 8*8 ! 拷贝前8个描述符
copygdt:
eseg movb al, (esi) ! eseg说明操作数的段寄存器为es
movb (ebx), al
inc esi
inc ebx
loop copygdt
mov eax, (_gdt+DS_SELECTOR+2) ! 设置GDT基址
and eax, 0x00FFFFFF
add eax, _gdt
mov (_gdt+GDT_SELECTOR+2), eax
lgdt (_gdt+GDT_SELECTOR) ! 加载GDT
! 设置内核段寄存器和栈
mov ebx, 8(ebp) ! boot参数地址
mov edx, 12(ebp) ! boot参数长度
mov eax, 16(ebp) ! a.out头地址
mov (_aout), eax
mov ax, ds
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, k_stktop
! 调用start.c文件中的cstart函数来保存内核信息
push edx
push ebx
push SS_SELECTOR
push DS_SELECTOR
push CS_SELECTOR
call _cstart
add esp, 5*4
! 重新加载GDT和IDT
lgdt (_gdt+GDT_SELECTOR)
lidt (_gdt+IDT_SELECTOR)
jmpf CS_SELECTOR:csinit ! 开始使用刚初始化好的表
csinit:
! 初始化段选择子
o16 mov ax, DS_SELECTOR ! o16说明操作数为16位
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
o16 mov ax, TSS_SELECTOR ! 初始化任务状态段(TSS)
ltr ax
push 0 ! 将eflags清0
popf
! 调用main.c文件中的main函数运行boot image
jmp _main |
其中,start.c文件中的cstart函数实现如下:PUBLIC void cstart(cs, ds, mds, parmoff, parmsize)
U16_t cs, ds; [color=#FF9900>// 内核代码段与数据段
U16_t mds; [color=#FF9900>// boot数据段
U16_t parmoff, parmsize; [color=#FF9900>// boot参数地址与长度
{
char params[128*sizeof(char *)];
register char *value;
extern int etext, end;
#if _WORD_SIZE != 2
[color=#FF9900>// 如果是386+则设置保护模式
machine.protected = 1;
#endif
[color=#FF9900>// 保存内核代码段和数据地址与长度
kinfo.code_base = seg2phys(cs);
kinfo.code_size = (phys_bytes) &etext;
kinfo.data_base = seg2phys(ds);
kinfo.data_size = (phys_bytes) &end;
[color=#FF9900>// 初始化保护模式下的描述符
prot_init();
[color=#FF9900>// 将boot参数拷贝到本地
kinfo.params_base = seg2phys(mds) + parmoff; [color=#FF9900>// boot参数地址
kinfo.params_size = MIN(parmsize,sizeof(params)-2); [color=#FF9900>// boot参数长度
phys_copy(kinfo.params_base, vir2phys(params), kinfo.params_size);
[color=#FF9900>// 源 目的 长度
[color=#FF9900>// 保存其他信息
kinfo.nr_procs = NR_PROCS;
kinfo.nr_tasks = NR_TASKS;
strncpy(kinfo.release, OS_RELEASE, sizeof(kinfo.release)); [color=#FF9900>// 目的 源 长度
kinfo.release[sizeof(kinfo.release)-1] = '\0';
strncpy(kinfo.version, OS_VERSION, sizeof(kinfo.version));
kinfo.version[sizeof(kinfo.version)-1] = '\0';
kinfo.proc_addr = (vir_bytes) proc;
kinfo.kmem_base = vir2phys(0);
kinfo.kmem_size = (phys_bytes) &end;
[color=#FF9900>// 获取CPU类型
machine.processor=atoi(get_value(params, "processor"));
#if _WORD_SIZE == 2
machine.protected = machine.processor >= 286;
#endif
if (!machine.protected) mon_return = 0;
[color=#FF9900>// 获取总线类型
value = get_value(params, "bus");
if (value == NIL_PTR || strcmp(value, "at") == 0) {
machine.pc_at = TRUE;
} else if (strcmp(value, "mca") == 0) {
machine.pc_at = machine.ps_mca = TRUE;
}
[color=#FF9900>// 获取显示器类型
value = get_value(params, "video");
if (strcmp(value, "ega") == 0) machine.vdu_ega = TRUE;
if (strcmp(value, "vga") == 0) machine.vdu_vga = machine.vdu_ega = TRUE;
} |
|
|