为了执行linux内的C语言main函数,上一篇讲到了,为了从汇编语言环境跳转到C语言环境下执行,将CPU工作模式从16位转变到32位模式(C语言是32位的),并且重新建立了GDT与IDT,但是此时GDT和IDT中并没有内容,所以不能进行内存寻址与中断,接下来就是初始化GDT和IDT了。
进入32位模式后,寄存器也将变为32位寄存器,下面的汇编语法和之前的intel汇编有些不同,为AT&T汇编,至于差别不在赘述。
Head.S startup_32: //重设段寄存器内容 movl $0x10,%eax mov %ax,%ds mov %ax,%es mov %ax,%fs mov %ax,%gs //初始化栈指针,即将栈顶指针指向栈底 lss _stack_start,%esp //建立IDT call setup_idt //建立GDT call setup_gdt //建立好GDT和IDT后重新设置段寄存器 movl $0x10,%eax mov %ax,%ds mov %ax,%es mov %ax,%fs mov %ax,%gs lss _stack_start,%esp xorl %eax,%eax //检查A20是否成功打开 1: incl %eax movl %eax,0x000000 cmpl %eax,0x100000 je 1b //检查8087数学协处理器后,开始进行内存内页 call check_x87 jmp after_page_tables after_page_tables: //以下为将main函数的参数入栈 pushl $0 pushl $0 pushl $0 pushl $L6 //将main函数的地址入栈 pushl $_main //开始进行内存分页 jmp setup_paging
上面的push _main操作很重要,因为在之后的ret指令后将会将栈中的_main函数地址装入cs,和ip中,参数也会装载到相应的寄存器中,这些操作都将在setup_paging后完成
setup_paging: //初始化前5k的内存 movl $1024*5,%ecx xorl %eax,%eax xorl %edi,%edi cld; rep; stosl //将0x0000处赋值为0x1000 + 7,7的意思是111代表访问权限,0x1000作为页表基地址,接下来就分别映射0x2000,0x3000,0x4000 movl $pg0+7,_pg_dir movl $pg1+7,_pg_dir+4 movl $pg2+7,_pg_dir+8 movl $pg3+7,_pg_dir+12 movl $pg3+4092,%edi movl $0xfff007,%eax //上述代码其实是建立4096字节的内核页表,每一个字节可以寻址到一个4k的页面,所以可以寻址的空间为16M std 1: stosl subl $0x1000,%eax jge 1b xorl %eax,%eax movl %eax,%cr3 movl %cr0,%eax orl $0x80000000,%eax movl %eax,%cr0 //调用ret开始执行main函数 ret
原文地址:http://blog.csdn.net/yang6816110/article/details/39457975