Chinaunix首页 | 论坛 | 博客
  • 博客访问: 303190
  • 博文数量: 94
  • 博客积分: 2163
  • 博客等级: 大尉
  • 技术积分: 932
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-20 09:23
文章存档

2012年(2)

2011年(92)

分类: LINUX

2011-09-20 16:59:10

  从head.s到main()函数。原来对于内核来说,在执行了初始化之后,内核执行权切换到了用户模式(0),cpu从0特权级切换到了3特权级。这是和原来想的不同。
  言归正传,在boot/目录中有bootsect.s、setup.s、head.s按照执行顺序来的。
 
总体功能:Pc开机,80x86进入实模式,并从地址0xffff0(此处是rom-bios中的地址)。Pc的bios将指向系统检测,从物理地址0初始化中断响亮。然后启动设备的第一扇区磁盘引导区(此就是在文件系统中描述的那个bootblock块)读入内存绝对地址0x7c00处(此时为实模式,汇编语言可以访问直接地址)。然后跳转至此。启动设备。
  bootsect.s由bios读入内存,然后自己将自己移动到0x90000处,启动设备后2kb代码读到0x90200处,
内核其他模块被读到0x10000开始处。
在系统显示loading....时,控制权将给boot/setup.s。启动部分识别主机及vga卡类型,然后将系统地址从0x10000移动到0x0000处,进入保护模式。
此时idt 、gdt、ldt被加载,分页工作也设置好了(从这里可以看出分页工作是head.s在中完成的,具体的内存分配则是完成从虚拟地址到物理地址映射的一个过程。),处理器和协处理器最终调用init/main.c的程序。
  不直接移动到0x0000开始处呢?因为在setup程序开始处还需要利用ROM-BIOS的中断调用来获取机器的参数。BIOS在初始化时会在物理内存开始处放置0x400的向量表,使用完后才可以覆盖。

/linux/boot/bootsect.s中的重要代码
其中都是一些BIOS中断用汇编语言编写,可见汇编的不可取代的位置。
jmpi go,INITSEG /*这条指令后段地址cs是0x9000。
seg cs
mov  sectors , cx/*将cx寄存器的值送入cs:sectors;cs是0x9000。*/
......
seg cs
mov bx,sectors /*cs : sectors的值送入bx寄存器。*/
root_defined:
seg cs
mov  root_dev,ax /*保存,后面有.org 508(0x1fc开始)。:.word ROOT_DEV存放根文件系统的设备号(其在启动扇区的508开始的第二个字节*/
boot_flags:
            .word 0x55aa   /*硬盘有效标识符*/
setup.s
也是利用rom bios读取机器系统数据。并保存在0x90000开始的位置,覆盖了bootsect程序的地方。比较重要的有0x90080第一个硬盘参数表(长度16个字节),0x90090第二个硬盘参数表,0x901fc是根设备号2个字节。
setup程序重要代码。
mov ah,#0x88
int 0x15
mov [2],ax 将扩展内存数值放在0x90002处。(一个字)
在取硬盘信息时
mov ax ,#0x000
mov ds, ax
lds  si,[4*0x41]取向量中断0x41的值,也即hd0的参数表地址->ds:si
mov ax,#INITSEG       // (0x9000)
mov es, ax
mov di,#0x0080 /*传输目的地址:0x9000:0x0080 es:di
mov cx,#0x10 //传输0x10字节*/
rep
movsb

.....
这里有
end_move:
      mov ax, #SETUPSEG/*#define SETUPSEG 0x9020*/
      mov  ds,ax
      lidt  idt_48/*加载中断描述符(idt)寄存器idt_48是6字节操作数,钱粮字节是限长,后字节是idt表长*/
      lgdt dgt_48 /* 加载全局描述符寄存器*/
.word 0x00eb,0x00eb /*jmp $ + 2, jmp $ + 2,跳转值是0,还是直接执行。*/
设置进入32保护模式运行:
mov ax,#0x0001  /*CR0寄存器比特位是0,导致cpu工作在保护模式.*/
lmsw   ax  /*加载机器状态字*/
jmpi   0, 8 /*跳转到cs段8,偏移值为0的地方,此时段8已经是保护模式下的段选择符了,段选择符为2字节。0x0000 0000, 0000 , 1000表示请求特权级0,使用全局描述符表的第一项,基地址是0*/
gdt:          /*此处是全局描述符表的开始,由多个8字节常的描述符项组成*/
.word 0, 0, 0, 0

/*用的是它,当加载代码段寄存器时,使用的是这个偏移值*/
.word 0x07ff /*8M Limit*/
.word 0x0000 /*基址是0*/
.word 0x9a00
.word 0x00c0

/*gdt表中偏移量是0x10,加载数据段寄存器(ds)时,使用的是这个偏移值*/
.word  0x7fff
.word  0x0000
.word  0x9200
.wrod  0x00c0

idt_48:
.word 0
.word 0 , 0 /*idt_base = 0L

gdt_48:
.word 0x800  /*gdt limit = 2048,256 项*/
.word 512 + gdt, 0x9 /*gdt_base = 0x9xxxx,用来表示此描述符所在内存的地址*/

因为代码段描述符和数据段描述符都指向系统模块的开始处,是物理地址0x0000,执行jmpi 0, 8(就会跳到基地址为0,偏移地址为0的地方执行,在段描述符项中段限制是检测用的看是否访问越位。)*/head.s程序开始执行。

head.s汇编和前面的语法不同,采用了AT&T汇编语言格式,使用GNU的gas和gld2进行汇编连接。
此程序在内存绝对地址为0的地方。功能加载数据段寄存器,重新设置终端描述符idt,共有256项。然后重新设置全局描述符表gdt.检测A20线是否开启。分页处理机制,将页面目录表放在绝对地址0开始处,此被覆盖,后放置可以寻址16M的4个也表,分别设置表项。利用返回指令将预先放置在堆栈的init/main.c程序入口弹出,运行main()程序。
重要代码:
movl $0x10, %eax 表示段选择符请求的是数据段描述符。
......
lss _stack_start, %esp /*_stack_start -> ss: esp*/
call setup_idt  /*调用设置中断描述符表子程序*/
call setup_gdt  /*调用设置全局描述符表子程序*/
setup _idt :
lea  ignore_int, %edx  /*将ignore_int的有效地址值->edx寄存器 ignore_int 是中断门*/
movl  $0x00080000, %eax /*将选择符0x0008置入%eax的高16位中。*/
movw  %dx, %ax  /*偏移值的低16位置入eax的低十六位中。此时eax含有门描述符低4字节的值*/
movw $0x8e00, %dx /*吃食edx含有门描述符高4字节的值*.
lea  _idt, %edi/*中断描述符的地址*/
mov  $256 %ecx;
rp_sidt:
movl  %eax,(%edi) /*将哑中断描述符存入表中(%edi)为描述符的地址*/
movl %edx, 4(%edi)/*
add $8,%edi /*edi指向下一项*/
dec %ecx  /*运行256次的意义是啥,因为是哑的*/
jne rp_sidt
lidt ldt_descr /*加载终端描述符寄存器值*/
ret;
setup_gdt:
lgdt  gdt_descr  /*加载全局描述符寄存器*/
      ret

.org 0x1000  /*从偏移0x1000开始的是第一个页表*/
pg0:

.org 0x2000
pg1:

.org 0x3000
pg2:

.org 0x4000
pg3:

org 0x5000/*内存数据块从偏移0x5000开始*/

_tmp_flopy_area:
.fill 1024 , 1, 0 /*共保留1024项,每项1字节,填充0*/
.......
setup_paging:
movl $1024 * 5 ,%ecx/*首先对1页目录4页也表)清零*/
xorl  %eax, %eax
xorl  %edi, %edi             /*页目录从0x000地址开始*/
cld  ; rep; stosl

movl $pg0+7, _pg_dir /*$pg0 + 7表示0x00001007是页目录的第一项*/
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 /*最后一项对应物理内存页面的地址是0xfff000,加上属性是0xfff007。
std /*edi 值递减(4字节)
stosl 
subl %0x1000, %eax  /*每添好一项物理值减0x1000。
jge 1b  /*如果小于0,那么己说明全填好了*/
xorl %eax,%eax
movl %eax,%cr4
/*设置页目录基地址寄存器cr3的值*/
movl %cr0, %eax
orl $0x80000000, %eax/*添加pg标志位*/
movl %eax,%cr0
ret

.....
.align 2
.word 0
gdt_descr:
            .word 256 * 8 -1
            .long _gdt
        .align 3
_idt: .fill 256, 8, 0    /*256项每项8字节,都是0*/

_gdt: .quad  0x0000000000000000
      .quad  0x00c09a0000000fff /*0x08,内核代码段最大长度是16M*/
      .quad  0x00c0920000000fff/*0x10,内核数据段最大长度是16M*/
      .quad  0x0000000000000000
      fill 252 , 8, 0 /*其他的为TSS段和LDT段*/
阅读(1994) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~