代码阅读报告(1)
涂李傲 2010013234软01
目录
1:阅读内容
2:代码阅读
2.1 asm.h
2.2 bootasm.s
2.3 x86.h
2.4 elf.h
2.5 bootmain.c
3:操作系统启动引导的流程分析
4:简答题
5:阅读心得
1:阅读内容
本次阅读包含以下几个文件:
asm.h:是bootasm.s汇编文件所需要的头文件,主要是一些与X86保护模式的段访问方式相关的宏定义。
bootasm.s:定义并实现了bootloader最先执行的函数start,此函数进行了一定的初始化,完成了从实模式到保护模式的转换,并调用bootmain.c中的bootmain函数。
types.h:包含一些无符号整型的缩写定义。
x86.h:一些用GNU C 嵌入式汇编实现的C 函数。
elf.h:定义了ELF 文件的结构。
bootmain.c:定义并实现了bootmain 函数。
2:代码阅读
2.1.asm.h
该文件是bootasm.s汇编文件所需要的头文件,主要是一些段描述符的宏定义。
(1)敛容
#define SEG_NULLASM \
.word 0, 0; \
.byte 0, 0, 0, 0 \
代码功能:把段(长度为8字节)基地址和大小都置为0(感觉有点像null~),然后在bootasm.h中的
gdt:
SEG_NULLASM # null g
调用了这个函数,构建了空段。
(2)
#define SEG_ASM(type,ba,lim) \
.word (((lim) >> 12) & 0xffff), ((ba) & 0xffff); \可爱女孩头像萌图片
.byte (((ba) >> 16) & 0xff), (0x90 | (type)), \
(0xC0 | (((lim) >> 28) & 0xf)), (((ba) >> 24) & 0xff)
代码功能:定义了长度为8字节的一般段。
(3)
#define STA_X 0x8 // Executable gment
#define STA_E 0x4 // Expand down (non-executable gments)
瘦肚子方法
#define STA_C 0x4 // Conforming code gment (executable only)
#define STA_W 0x2 // Writeable (non-executable gments)
#define STA_R 0x2 // Readable (executable gments)cpu过高
#define STA_A 0x1 // Accesd
定义了段的六种类型
2.2.bootasm.s
该文件保存于硬盘设备的第一个扇区中,在开机的时候由BIOS加载到物理内存的0x7C00~0x7CFF处,然后在”real mode”下开始执行,此时有%CS=0 %IP=0x7C00
(1)
start:
cli # Disable interrupts
此处开始进入start函数,先发出一条指令“cli”,那么它的用处是关闭中断。这里我的理解是BIOS就像一个小的CPU,它自己会产生中断,但是这个时候BIOS已经运行完了,如果放任这些中断存在的话就会给硬件提供“interrupt handlers”,但是这个时候存在“interrupt handlers”是不安全的,所以我们要关闭中断,等到x86准备好了再重新恢复中断。
(2)
xorw %ax,%ax # Segment number zero
movw %ax,%ds # -> Data Segment
movw %ax,%es # -> Extra Segment
movw %ax,%ss # -> Stack Segment
由于在BIOS中寄存器的值是不可知的,所以为了后面能够正常运行,在这里需要将寄存器进行初始化,这样代码就很直观了。就是先将寄存器%ax置为0,再将0值赋给%ds,%es和%ss
(3)
ta20.1:
inb $0x64,%al # Wait for not busy
testb $0x2,%al
jnz ta20.1
movb $0xd1,%al # 0xd1 -> port 0x64
outb %al,$0x64
ta20.2:
inb $0x64,%al # Wait for not busy
testb $0x2,%al
jnz ta20.2
movb $0xdf,%al # 0xdf -> port 0x60
outb %al,$0x60
无可替代的英文
根据x86相关文档,gment:offt模式可以表示21位的物理地址,但是 Intel 8088 只支持 20位内存地址,但是通过段-偏移方式可以支持21位内存地址。这样一来 Intel 8088 会将自动丢弃最高位的数据从而出现错误。IBM为了避免这个问题,于是提供了一种兼容方式:当键盘控制器输出端口的第 2位为低位时, A20始终清零,而当其置为高位时,A20可以正常使用。
在这段代码中通过检测0x64寄存器中的第二位是否为1来判断键盘缓存中是否有数据。当键盘空闲(第二位为0时)的时候向0x64寄存器中写入0xd1(表示下一个写入 0x60 端口的数据将被置于 8042 键盘控制器的输出端口),向0x60寄存器中写入0xdf(将键盘控制端口的第二位置为1)打开A20地址线,这样就可以使用21位地址啦!
群管理员
(4)
lgdt gdtdesc
movl %cr0, %eax
orl $CR0_PE, %eax
movl %eax, %cr0
第一行的作用是将GDT的地址设置为gdtdesc。一旦装载了GDT 寄存器,扇区就会通过将CR0_PE 装入寄存器cr0开启“protected mode”。后面三行的作用则是置%cr0 寄存器的 PE 位。$CR0_PE=1,因此,这三行是置%cr0 寄存器的第 0 位为 1,该位为 0 时,CPU 运行于“real mode”,为 1 时,CPU 运行于“protected mode”。当第四行执行完成后,CPU 就运行在保护模式下了。
(5)莫尔道嘎
ljmp $(SEG_KCODE<<3), $start32
这一行因为有很长的注释,所以显得很直观。由于在进入“protected mode”的时候不会让系统进入32位模式,所以设置这么一个触发事件,将程序从16位转换到32位模式下。
(6)
start32:
# Set up the protected-mode data gment registers
movw $(SEG_KDATA<<3), %ax # Our data gment lector
movw %ax, %ds # -> DS: Data Segment
movw %ax, %es # -> ES: Extra Segment
movw %ax, %ss # -> SS: Stack Segment
movw $0, %ax # Zero gments not ready for u
movw %ax, %fs # -> FS
movw %ax, %gs # -> GS
连连看20合1 这一块的作用和bootasm.s最前面一块的作用基本是类似的,就不多说了。
(7)
movl $start, %esp
call bootmain
将$start的地址(0x7C00)赋值给%esp寄存器,将0x7C00设置为栈顶,之后栈指针就会从 0x7c00 开始递减,而不会覆盖掉启动扇区的代码。而后面那一行就要开始调用bootmain函数了,这个函数到目前为止还没有出现,所以我会在后面详细说。
(8)
movw $0x8a00, %ax # 0x8a00 -> port 0x8a00
movw %ax, %dx
outw %ax, %dx
movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00
outw %ax, %dx
pin:
jmp spin
这段代码从boot.pdf上来看好像是用来处理错误的,一般情况下我们不会调用,而一旦调用就会出现死循环,这个时候有这么一句话:“A real boot ctor might attempt to print an error message first”。估计这样的直接结果就是重新启动计算机吧……
好的,现在我们已经把bootasm.s给过了一遍,感觉总体上和boot.pdf讲的差不多,接下来我们来看看几个头文件~
2.3 x86.h
该头文件包含了嵌入式汇编的C函数,供 C程序使用汇编指令。由于在第一次作业中不是所有定义过的函数都出现了,所以下面我只介绍在bootmain.c中被用到的函数;
(1)
staticinline uchar
inb(ushort port)
{
uchar data;
asm volatile("in %1,%0" : "=a" (data) : "d" (port));