2.html

来自「linux 0.11中文版 有注释」· HTML 代码 · 共 358 行 · 第 1/2 页

HTML
358
字号
<html>
<head>
<title>boot/head.s</title>
<meta name='robots' content='noindex,nofollow'>
<meta name='generator' content='GLOBAL-5.4.1'>
</head>
<body text='#191970' bgcolor='#f5f5dc' vlink='gray'>
<a name='TOP'><h2><a href='../mains.html'>root</a>/<a href='../files/97.html'>boot</a>/head.s</h2>
<i><font color='green'>/* [&lt;][&gt;][^][v][top]<a href='#BOTTOM'>[bottom]</a><a href='../mains.html'>[index]</a><a href='../help.html'>[help]</a> */</font></i>
<hr>
<pre>
<a name='L1'><i><font color='green'>/*</font></i>
<a name='L2'><i><font color='green'>* linux/boot/head.s</font></i>
<a name='L3'><i><font color='green'>*</font></i>
<a name='L4'><i><font color='green'>* (C) 1991 Linus Torvalds</font></i>
<a name='L5'><i><font color='green'>*/</font></i>
<a name='L6'>
<a name='L7'><i><font color='green'>/*</font></i>
<a name='L8'><i><font color='green'>* head.s contains the 32-bit startup code.</font></i>
<a name='L9'><i><font color='green'>*</font></i>
<a name='L10'><i><font color='green'>* NOTE!!! Startup happens at absolute address 0x00000000, which is also where</font></i>
<a name='L11'><i><font color='green'>* the page directory will exist. The startup code will be overwritten by</font></i>
<a name='L12'><i><font color='green'>* the page directory.</font></i>
<a name='L13'><i><font color='green'>*/</font></i>
<a name='L14'><i><font color='green'>/*</font></i>
<a name='L15'><i><font color='green'>* head.s 含有32 位启动代码。</font></i>
<a name='L16'><i><font color='green'>* 注意!!! 32 位启动代码是从绝对地址0x00000000 开始的,这里也同样是页目录将存在的地方,</font></i>
<a name='L17'><i><font color='green'>* 因此这里的启动代码将被页目录覆盖掉。</font></i>
<a name='L18'><i><font color='green'>*/</font></i>
<a name='L19'>.text
<a name='L20'>.globl _idt,_gdt,_pg_dir,_tmp_floppy_area
<a name='L21'>_pg_dir: # 页目录将会存放在这里。
<a name='L22'>startup_32: # 18-22 行设置各个数据段寄存器。
<a name='L23'>movl $0x10,%eax # 对于GNU 汇编来说,每个直接数要以'$'开始,否则是表示地址。
<a name='L24'><i><font color='green'># 每个寄存器名都要以'%'开头,eax 表示是32 位的ax 寄存器。</font></i>
<a name='L25'><i><font color='green'># 再次注意!!! 这里已经处于32 位运行模式,因此这里的$0x10 并不是把地址0x10 装入各个</font></i>
<a name='L26'><i><font color='green'># 段寄存器,它现在其实是全局段描述符表中的偏移值,或者更正确地说是一个描述符表项</font></i>
<a name='L27'><i><font color='green'># 的选择符。有关选择符的说明请参见setup.s 中193 行下的说明。这里$0x10 的含义是请求</font></i>
<a name='L28'><i><font color='green'># 特权级0(位0-1=0)、选择全局描述符表(位2=0)、选择表中第2 项(位3-15=2)。它正好指</font></i>
<a name='L29'>在当前的Linux 操作系统中,gas 和gld 已经分别更名为as 和ld。
<a name='L30'><i><font color='green'># 向表中的数据段描述符项。(描述符的具体数值参见前面setup.s 中212,213 行)</font></i>
<a name='L31'><i><font color='green'># 下面代码的含义是:置ds,es,fs,gs 中的选择符为setup.s 中构造的数据段(全局段描述符表</font></i>
<a name='L32'><i><font color='green'># 的第2 项)=0x10,并将堆栈放置在数据段中的_stack_start 数组内,然后使用新的中断描述</font></i>
<a name='L33'><i><font color='green'># 符表和全局段描述表.新的全局段描述表中初始内容与setup.s 中的完全一样。</font></i>
<a name='L34'>mov %ax,%ds
<a name='L35'>mov %ax,%es
<a name='L36'>mov %ax,%fs
<a name='L37'>mov %ax,%gs
<a name='L38'>lss _stack_start,%esp # 表示_stack_start??ss:esp,设置系统堆栈。
<a name='L39'><i><font color='green'># stack_start 定义在kernel/sched.c,69 行。</font></i>
<a name='L40'>call setup_idt # 调用设置中断描述符表子程序。
<a name='L41'>call setup_gdt # 调用设置全局描述符表子程序。
<a name='L42'>movl $0x10,%eax # reload all the segment registers
<a name='L43'>mov %ax,%ds # after changing gdt. CS was already
<a name='L44'>mov %ax,%es # reloaded in 'setup_gdt'
<a name='L45'>mov %ax,%fs # 因为修改了gdt,所以需要重新装载所有的段寄存器。
<a name='L46'>mov %ax,%gs # CS 代码段寄存器已经在setup_gdt 中重新加载过了。
<a name='L47'>lss _stack_start,%esp
<a name='L48'><font color='darkred'>#</font> 32-36 行用于测试A20 地址线是否已经开启。采用的方法是向内存地址0x000000 处写入任意
<a name='L49'><i><font color='green'># 一个数值,然后看内存地址0x100000(1M)处是否也是这个数值。如果一直相同的话,就一直</font></i>
<a name='L50'><i><font color='green'># 比较下去,也即死循环、死机。表示地址A20 线没有选通,结果内核就不能使用1M 以上内存。</font></i>
<a name='L51'>xorl %eax,%eax
<a name='L52'>1: incl %eax # check that A20 really IS enabled
<a name='L53'>movl %eax,0x000000 # loop forever if it isn't
<a name='L54'>cmpl %eax,0x100000
<a name='L55'>je 1b # '1b'表示向后(backward)跳转到标号1 去(33 行)。
<a name='L56'><i><font color='green'># 若是'5f'则表示向前(forward)跳转到标号5 去。</font></i>
<a name='L57'><i><font color='green'>/*</font></i>
<a name='L58'><i><font color='green'>* NOTE! 486 should set bit 16, to check for write-protect in supervisor</font></i>
<a name='L59'><i><font color='green'>* mode. Then it would be unnecessary with the "verify_area()"-calls.</font></i>
<a name='L60'><i><font color='green'>* 486 users probably want to set the NE (#5) bit also, so as to use</font></i>
<a name='L61'><i><font color='green'>* int 16 for math errors.</font></i>
<a name='L62'><i><font color='green'>*/</font></i>
<a name='L63'><i><font color='green'>/*</font></i>
<a name='L64'><i><font color='green'>* 注意! 在下面这段程序中,486 应该将位16 置位,以检查在超级用户模式下的写保护,</font></i>
<a name='L65'><i><font color='green'>* 此后"verify_area()"调用中就不需要了。486 的用户通常也会想将NE(#5)置位,以便</font></i>
<a name='L66'><i><font color='green'>* 对数学协处理器的出错使用int 16。</font></i>
<a name='L67'><i><font color='green'>*/</font></i>
<a name='L68'><i><font color='green'># 下面这段程序(43-65)用于检查数学协处理器芯片是否存在。方法是修改控制寄存器CR0,在</font></i>
<a name='L69'><i><font color='green'># 假设存在协处理器的情况下执行一个协处理器指令,如果出错的话则说明协处理器芯片不存在,</font></i>
<a name='L70'><i><font color='green'># 需要设置CR0 中的协处理器仿真位EM(位2),并复位协处理器存在标志MP(位1)。</font></i>
<a name='L71'>movl %cr0,%eax # check math chip
<a name='L72'>andl $0x80000011,%eax # Save PG,PE,ET
<a name='L73'><i><font color='green'>/* "orl $0x10020,%eax" here for 486 might be good */</font></i>
<a name='L74'>orl $2,%eax # set MP
<a name='L75'>movl %eax,%cr0
<a name='L76'>call check_x87
<a name='L77'>jmp after_page_tables # 跳转到135 行。
<a name='L78'>
<a name='L79'><i><font color='green'>/*</font></i>
<a name='L80'><i><font color='green'>* We depend on ET to be correct. This checks for 287/387.</font></i>
<a name='L81'><i><font color='green'>*/</font></i>
<a name='L82'><i><font color='green'>/*</font></i>
<a name='L83'><i><font color='green'>* 我们依赖于ET 标志的正确性来检测287/387 存在与否。</font></i>
<a name='L84'><i><font color='green'>*/</font></i>
<a name='L85'>check_x87:
<a name='L86'>fninit
<a name='L87'>fstsw %ax
<a name='L88'>cmpb $0,%al
<a name='L89'>je 1f <i><font color='green'>/* no coprocessor: have to set bits */</font></i>
<a name='L90'>movl %cr0,%eax # 如果存在的则向前跳转到标号1 处,否则改写cr0。
<a name='L91'>xorl $6,%eax <i><font color='green'>/* reset MP, set EM */</font></i>
<a name='L92'>movl %eax,%cr0
<a name='L93'>ret
<a name='L94'>.align 2 # 这里".align 2"的含义是指存储边界对齐调整。"2"表示调整到地址最后2 位为零,
<a name='L95'><i><font color='green'># 即按4 字节方式对齐内存地址。</font></i>
<a name='L96'>1: .byte 0xDB,0xE4 <i><font color='green'>/* fsetpm for 287, ignored by 387 */</font></i> # 287 协处理器码。
<a name='L97'>ret
<a name='L98'>
<a name='L99'><i><font color='green'>/*</font></i>
<a name='L100'><i><font color='green'>* setup_idt</font></i>
<a name='L101'><i><font color='green'>*</font></i>
<a name='L102'><i><font color='green'>* sets up a idt with 256 entries pointing to</font></i>
<a name='L103'><i><font color='green'>* ignore_int, interrupt gates. It then loads</font></i>
<a name='L104'><i><font color='green'>* idt. Everything that wants to install itself</font></i>
<a name='L105'><i><font color='green'>* in the idt-table may do so themselves. Interrupts</font></i>
<a name='L106'><i><font color='green'>* are enabled elsewhere, when we can be relatively</font></i>
<a name='L107'><i><font color='green'>* sure everything is ok. This routine will be over-</font></i>
<a name='L108'><i><font color='green'>* written by the page tables.</font></i>
<a name='L109'><i><font color='green'>*/</font></i>
<a name='L110'><i><font color='green'>/*</font></i>
<a name='L111'><i><font color='green'>* 下面这段是设置中断描述符表子程序 setup_idt</font></i>
<a name='L112'><i><font color='green'>*</font></i>
<a name='L113'><i><font color='green'>* 将中断描述符表idt 设置成具有256 个项,并都指向ignore_int 中断门。然后加载中断</font></i>
<a name='L114'><i><font color='green'>* 描述符表寄存器(用lidt 指令)。真正实用的中断门以后再安装。当我们在其它地方认为一切</font></i>
<a name='L115'><i><font color='green'>* 都正常时再开启中断。该子程序将会被页表覆盖掉。</font></i>
<a name='L116'><i><font color='green'>*/</font></i>
<a name='L117'><i><font color='green'># 中断描述符表中的项虽然也是8 字节组成,但其格式与全局表中的不同,被称为门描述符</font></i>
<a name='L118'><font color='darkred'>#</font> (Gate Descriptor)。它的0-1,6-7 字节是偏移量,2-3 字节是选择符,4-5 字节是一些标志。
<a name='L119'>setup_idt:
<a name='L120'>lea ignore_int,%edx # 将ignore_int 的有效地址(偏移值)值??edx 寄存器
<a name='L121'>movl $0x00080000,%eax # 将选择符0x0008 置入eax 的高16 位中。
<a name='L122'>movw %dx,%ax <i><font color='green'>/* selector = 0x0008 = cs */</font></i>
<a name='L123'><i><font color='green'># 偏移值的低16 位置入eax 的低16 位中。此时eax 含有</font></i>
<a name='L124'><i><font color='green'>#门描述符低4 字节的值。</font></i>
<a name='L125'>movw $0x8E00,%dx <i><font color='green'>/* interrupt gate - dpl=0, present */</font></i>
<a name='L126'><i><font color='green'># 此时edx 含有门描述符高4 字节的值。</font></i>
<a name='L127'>lea _idt,%edi # _idt 是中断描述符表的地址。
<a name='L128'>mov $256,%ecx
<a name='L129'>rp_sidt:
<a name='L130'>movl %eax,(%edi) # 将哑中断门描述符存入表中。
<a name='L131'>movl %edx,4(%edi)
<a name='L132'>addl $8,%edi # edi 指向表中下一项。
<a name='L133'>dec %ecx
<a name='L134'>jne rp_sidt
<a name='L135'>lidt idt_descr # 加载中断描述符表寄存器值。
<a name='L136'>ret
<a name='L137'>
<a name='L138'><i><font color='green'>/*</font></i>
<a name='L139'><i><font color='green'>* setup_gdt</font></i>
<a name='L140'><i><font color='green'>*</font></i>
<a name='L141'><i><font color='green'>* This routines sets up a new gdt and loads it.</font></i>
<a name='L142'><i><font color='green'>* Only two entries are currently built, the same</font></i>
<a name='L143'><i><font color='green'>* ones that were built in init.s. The routine</font></i>
<a name='L144'><i><font color='green'>* is VERY complicated at two whole lines, so this</font></i>
<a name='L145'><i><font color='green'>* rather long comment is certainly needed :-).</font></i>
<a name='L146'><i><font color='green'>* This routine will beoverwritten by the page tables.</font></i>
<a name='L147'><i><font color='green'>*/</font></i>
<a name='L148'><i><font color='green'>/*</font></i>
<a name='L149'><i><font color='green'>* 设置全局描述符表项 setup_gdt</font></i>
<a name='L150'><i><font color='green'>* 这个子程序设置一个新的全局描述符表gdt,并加载。此时仅创建了两个表项,与前</font></i>
<a name='L151'><i><font color='green'>* 面的一样。该子程序只有两行,“非常的”复杂,所以当然需要这么长的注释了?。</font></i>
<a name='L152'><i><font color='green'>setup_gdt:</font></i>
<a name='L153'><i><font color='green'>lgdt gdt_descr # 加载全局描述符表寄存器(内容已设置好,见232-238 行)。</font></i>
<a name='L154'><i><font color='green'>ret</font></i>
<a name='L155'><i><font color='green'></font></i>
<a name='L156'><i><font color='green'>/*</font></i>
<a name='L157'><i><font color='green'>* I put the kernel page tables right after the page directory,</font></i>
<a name='L158'><i><font color='green'>* using 4 of them to span 16 Mb of physical memory. People with</font></i>
<a name='L159'><i><font color='green'>* more than 16MB will have to expand this.</font></i>
<a name='L160'><i><font color='green'>*/</font></i>
<a name='L161'><i><font color='green'>/* Linus 将内核的内存页表直接放在页目录之后,使用了4 个表来寻址16 Mb 的物理内存。</font></i>
<a name='L162'><i><font color='green'>* 如果你有多于16 Mb 的内存,就需要在这里进行扩充修改。</font></i>
<a name='L163'><i><font color='green'>*/</font></i>
<a name='L164'><i><font color='green'># 每个页表长为4 Kb 字节,而每个页表项需要4 个字节,因此一个页表共可以存放1000 个表项,</font></i>
<a name='L165'><i><font color='green'># 如果一个表项寻址4 Kb 的地址空间,则一个页表就可以寻址4 Mb 的物理内存。</font></i>
<a name='L166'><i><font color='green'># 页表项的格式为:项的前0-11 位存放一些标志,如是否在内存中(P 位0)、读写许可(R/W 位1)、</font></i>
<a name='L167'><i><font color='green'># 普通用户还是超级用户使用(U/S 位2)、是否修改过(是否脏了)(D 位6)等;表项的位12-31 是</font></i>
<a name='L168'><i><font color='green'># 页框地址,用于指出一页内存的物理起始地址。</font></i>

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?