淘宝网站建设可信吗,收费网站空间,1 建设网站目的是什么意思,电商网站运维怎么做Linux-0.11 boot目录head.s详解
模块简介
在head.s中#xff0c;操作系统主要做了如下几件事#xff1a;
重新设置中断描述符和全局描述符检查A20地址线是否开启检查数学协处理器初始化页表并开启分页跳转到main函数执行
过程详解
重新设置IDT和GDT
在setup.s中我们已经…Linux-0.11 boot目录head.s详解
模块简介
在head.s中操作系统主要做了如下几件事
重新设置中断描述符和全局描述符检查A20地址线是否开启检查数学协处理器初始化页表并开启分页跳转到main函数执行
过程详解
重新设置IDT和GDT
在setup.s中我们已经设置过了IDT和GDT 为什么还要再设置一遍
因为setup.s中设置的IDT和GDT后面会被覆盖因此在head.S中会重新设置一遍。
startup_32:movl $0x10,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fsmov %ax,%gslss stack_start,%espcall setup_idt !设置中断call setup_gdt !设置全局描述符表movl $0x10,%eax # reload all the segment registersmov %ax,%ds # after changing gdt. CS was alreadymov %ax,%es # reloaded in setup_gdtmov %ax,%fsmov %ax,%gslss stack_start,%esp中断门描述符的格式如下所示: 检查A20地址线是否开启
下面用于检测A20地址线是否已经开启。 xorl %eax,%eax
1: incl %eax # check that A20 really IS enabledmovl %eax,0x000000 # loop forever if it isntcmpl %eax,0x100000je 1b检查数学协处理器
下面用于检查数学协处理器芯片是否存在 movl %cr0,%eax # check math chipandl $0x80000011,%eax # Save PG,PE,ET
/* orl $0x10020,%eax here for 486 might be good */orl $2,%eax # set MPmovl %eax,%cr0call check_x87jmp after_page_tables/** We depend on ET to be correct. This checks for 287/387.*/
check_x87:fninit !向协处理发出初始化命令fstsw %ax !取协处理器状态字到ax寄存器中cmpb $0,%alje 1f /* no coprocessor: have to set bits */movl %cr0,%eaxxorl $6,%eax /* reset MP, set EM */movl %eax,%cr0ret初始化页表并开启分页
下面这里将进行页表的安装安装的过程参考下面这张图
after_page_tables:pushl $0 # These are the parameters to main :-)pushl $0pushl $0pushl $L6 # return address for main, if it decides to.pushl $mainjmp setup_pagingsetup_paging:movl $1024*5,%ecx /* 5 pages - pg_dir4 page tables */xorl %eax,%eaxxorl %edi,%edi /* pg_dir is at 0x000 */cld;rep;stoslmovl $pg07,pg_dir /* set present bit/user r/w */movl $pg17,pg_dir4 /* --------- --------- */movl $pg27,pg_dir8 /* --------- --------- */movl $pg37,pg_dir12 /* --------- --------- */movl $pg34092,%edimovl $0xfff007,%eax /* 16Mb - 4096 7 (r/w user,p) */std
1: stosl /* fill pages backwards - more efficient :-) */subl $0x1000,%eaxjge 1bcldxorl %eax,%eax !设置页目录表基址寄存器cr3的值movl %eax,%cr3 movl %cr0,%eax !设置启动使用分页处理orl $0x80000000,%eaxmovl %eax,%cr0 /* set paging (PG) bit */ret /* this also flushes prefetch-queue */跳转到main函数执行
在setup_paging执行完毕之后会通过ret返回ret指令会将栈顶的内容弹出到PC指针中去执行。此时esp指向的位置存放的是main函数的地址。因此接下来会执行main函数。
注意到在将main入栈时还一同入栈了一些其他参数 pushl $0 # These are the parameters to main :-)pushl $0pushl $0pushl $L6这里就需要回顾一下c语言的调用规约如下图所示 因此这里可以得到L6是main函数的返回值。立即数000将会被作为main函数的入参。
接下来再看下面的代码就很清晰了实际就是在建立好页表的映射关系后就开始跳转到main函数去执行了(init/main.c)。
after_page_tables:pushl $0 # These are the parameters to main :-)pushl $0pushl $0pushl $L6 # return address for main, if it decides to.pushl $mainjmp setup_pagingsetup_paging:...retQ A
setup_paging在建立页表时会将head.s的部分代码覆盖怎么保证不会把正在执行的代码覆盖
可以通过反汇编查看一下system模块的内存分布
objdump -d tools/system如下所示
00000000 pg_dir:0: b8 10 00 00 00 mov $0x10,%eax5: 8e d8 mov %eax,%ds...
0000005a check_x87:5a: db e3 fninit 5c: 9b df e0 fstsw %ax5f: 3c 00 cmp $0x0,%al...
00000071 setup_idt:71: 8d 15 28 54 00 00 lea 0x5428,%edx77: b8 00 00 08 00 mov $0x80000,%eax...
0000008e rp_sidt:8e: 89 07 mov %eax,(%edi)90: 89 57 04 mov %edx,0x4(%edi)...
000000a1 setup_gdt:a1: 0f 01 15 b2 54 00 00 lgdtl 0x54b2a8: c3 ret ...
00001000 pg0:...00002000 pg1:...00003000 pg2:...00004000 pg3:...
00005000 tmp_floppy_area:...
00005400 after_page_tables:5400: 6a 00 push $0x05402: 6a 00 push $0x0...
00005412 L6:5412: eb fe jmp 5412 L6
00005414 int_msg:5414: 55 push %ebp5415: 6e outsb %ds:(%esi),(%dx)...
00005428 ignore_int:5428: 50 push %eax5429: 51 push %ecx...
0000544e setup_paging:544e: b9 00 14 00 00 mov $0x1400,%ecx5453: 31 c0 xor %eax,%eax5455: 31 ff xor ...
000054aa idt_descr:54aa: ff 07 incl (%edi)54ac: b8 54 00 00 00 mov $0x54,%eax...000054b2 gdt_descr:54b2: ff 07 incl (%edi)54b4: b8 .byte 0xb854b5: 5c pop %esp...000054b8 idt:...00005cb8 gdt:...5cc0: ff 0f decl (%edi)可以看到代码标号setup_page的起始地址是0000544e而内存页表和页目录表的地址范围是0x0000-0x5000。因此当程序执行到setup_page时将建立页目录表和页表 这将会覆盖0x0000-0x5000的部分代码即pg_dircheck_x87setup_idtrp_sidtsetup_gdt 并不会覆盖到setup_page的代码head.s在代码的分布计算上确实是费了一番功夫。 文中如有表达不正确之处欢迎大家与我交流