⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 1120.html

📁 著名的linux英雄站点的文档打包
💻 HTML
📖 第 1 页 / 共 5 页
字号:
and al, dl !! 取dl的低4比特值。<br>
add al, #0x90 ! 将al转换为ASCII十六进制码(4个指令)<br>
daa !! 十进制调整<br>
adc al, #0x40 !! (adc dest, src ==&gt; dest := dest + src + c )<br>
daa<br>
int 0x10<br>
loop print_digit<br>
ret<br>
<br>
<br>
/*<br>
* 这个过程(子程序)关闭软驱的马达,这样<br>
* 我们进入内核后它的状态就是已知的,以后也就<br>
* 不用担心它了。<br>
*/<br>
kill_motor:<br>
push dx<br>
mov dx,#0x3f2<br>
xor al,al<br>
outb<br>
pop dx<br>
ret<br>
<br>
!! 数据区<br>
sectors:<br>
.word 0 !! 当前每磁道扇区数。(36||18||15||9)<br>
<br>
disksizes: !! 每磁道扇区数表<br>
.byte 36, 18, 15, 9<br>
<br>
msg1:<br>
.byte 13, 10<br>
.ascii "Loading"<br>
<br>
.org 497 !! 从boot程序的二进制文件的497字节开始<br>
setup_sects:<br>
.byte SETUPSECS<br>
root_flags:<br>
.word CONFIG_ROOT_RDONLY<br>
syssize:<br>
.word SYSSIZE<br>
swap_dev:<br>
.word SWAP_DEV<br>
ram_size:<br>
.word RAMDISK<br>
vid_mode:<br>
.word SVGA_MODE<br>
root_dev:<br>
.word ROOT_DEV<br>
boot_flag: !! 分区启动标志<br>
.word 0xAA55<br>
<br>
<br>
<br>
<br>
<br>
[目录]<br>
<br>
--------------------------------------------------------------------------------<br>
<br>
<br>
setup.S<br>
<br>
1、按规定得有个头,所以一开始是惯用的JMP;<br>
2、头里边内容很丰富,具体用法走着瞧;<br>
3、自我检测,不知道有什么用,防伪造?防篡改?<br>
4、如果装载程序不对,只好死掉!以下终于走入正题;<br>
5、获取内存容量(使用了三种办法,其中的E820和E801看不明白,int 15倒是老朋友了--应该是上个世纪80年代末认识的了,真佩服十年过去了,情意依旧,不过遇上一些不守规矩的BIOS,不知道还行不行);<br>
6、将键盘重复键的重复率设为最大,灵敏一点?<br>
7、检测硬盘,不懂,放这里干什么?<br>
8、检测MCA总线(不要问我这是什么);<br>
9、检测PS/2鼠标,用int 11,只是不知道为何放这里;<br>
10、检测电源管理BIOS;唉,书到用时方恨少,不懂的太多了,真不好意思;不过也没有关系, 不懂的就别去动它就行了;以下要进入内核了;<br>
11、 在进入保护模式之前,可以调用一个你提供的试模式下的过程,让你最后在看她一眼,当然你要是不提供,那就有个默认的,无非是塞住耳朵闭上眼睛禁止任何中断,包括著名的NMI ;<br>
12、设置保护模式起动后的例程地址, 你可以写自己的例程,但不是代替而是把它加在setup提供的例程的前面(显示一个小鸭子?);<br>
13、如果内核是zImage, 将它移动到0x10000处;<br>
14、如果自己不在0x90000处,则移动到0x90000处;<br>
15、建立idt, gdt表;<br>
16、启动A20;<br>
17、屏住呼吸,屏闭所有中断;<br>
18、启动!movw $1, %ax ; lmsw %ax; 好已经进入保护模式下,马上进行局部调整;<br>
19、jmpi 0x100000, __KERNEL_CS,终于进入内核;<br>
setup.S<br>
A summary of the setup.S code 。The slight differences in the operation of setup.S due to a big kernel is documented here. When the switch to 32 bit protected mode begins the code32_start address is defined as 0x100000 (when loaded) here.<br>
code32_start:<br>
<br>
#ifndef __BIG_KERNEL__<br>
.long 0x1000<br>
#else<br>
.long 0x100000<br>
#endif<br>
<br>
After setting the keyboard repeat rate to a maximum, calling video.S, storing the video parameters, checking for the hard disks, PS/2 mouse, and APM BIOS the preparation for real mode switch begins.<br>
<br>
The interrupts are disabled. Since the loader changed the code32_start address, the code32 varable is updated. This would be used for the jmpi instruction when the setup.S finally jumps to compressed/head.S. In case of a big kernel this is loacted at 0x100000.<br>
<br>
seg cs<br>
mov eax, code32_start !modified above by the loader<br>
seg cs<br>
mov code32,eax<br>
<br>
!code32 contains the correct address to branch to after setup.S finishes After the above code there is a slight difference in the ways the big and small kernels are dealt. In case of a small kernel the kernel is moved down to segment address 0x100, but a big kernel is not moved. Before decompression, the big kernel stays at 0x100000. The following is the code that does thischeck.test byte ptr loadflags,<br>
<br>
#LOADED_HIGH<br>
jz do_move0 ! a normal low loaded zImage is moved<br>
jmp end_move ! skip move<br>
<br>
The interrupt and global descriptors are initialized:<br>
<br>
lidt idt_48 ! load idt wit 0,0<br>
lgdt gdt_48 ! load gdt with whatever appropriate<br>
<br>
After enabling A20 and reprogramming the interrupts, it is ready to set the PE bit:<br>
<br>
mov ax,#1<br>
lmsw ax<br>
jmp flush_instr<br>
flush_instr:<br>
xor bx.bx !flag to indicate a boot<br>
! Manual, mixing of 16-bit and 32 bit code<br>
db 0x166,0xea !prefix jmpi-opcode<br>
code32: dd ox1000 !this has been reset in caes of a big kernel, to 0x100000<br>
dw __KERNEL_CS<br>
<br>
Finally it prepares the opcode for jumping to compressed/head.S which in the big kernel is at 0x100000. The compressed kernel would start at 0x1000 in case of a small kernel.<br>
<br>
compressed/head.S<br>
<br>
When setup.S relinquishes control to compressed/head.S at beginning of the compressed kernmel at 0x100000. It checks to see if A20 is really enabled otherwise it loops forever.<br>
<br>
Itinitializes eflags, and clears BSS (Block Start by Symbol) creating reserved space for uninitialized static or global variables. Finally it reserves place for the moveparams structure (defined in misc.c) and pushes the current stack pointer on the stack and calls the C function decompress_kernel which takes a struct moveparams * as an argument<br>
<br>
subl $16,%esp<br>
pushl %esp<br>
call SYMBOL_NAME(decompress_kernel)<br>
orl ??,??<br>
jnz 3f<br>
<br>
Te C function decompress_kernel returns the variable high_loaded which is set to 1 in the function setup_output_buffer_if_we_run_high, which is called in decompressed_kernel if a big kernel was loaded.<br>
When decompressed_kernel returns, it jumps to 3f which moves the move routine.<br>
<br>
movl $move_routine_start,%esi ! puts the offset of the start of the source in the source index register<br>
mov $0x1000,?? ! the destination index now contains 0x1000, thus after move, the move routine starts at 0x1000<br>
movl $move_routine_end,??<br>
sub %esi,?? ! ecx register now contains the number of bytes to be moved<br>
! (number of bytes between the labels move_routine_start and move_routine_end)<br>
cld<br>
rep<br>
movsb ! moves the bytes from ds:si to es:di, in each loop it increments si and di, and decrements cx<br>
! the movs instruction moves till ecx is zero<br>
<br>
Thus the movsb instruction moves the bytes of the move routine between the labels move_routine_start and move_routine_end. At the end the entire move routine labeled move_routine_start is at 0x1000. The movsb instruction moves bytes from ds:si to es:si.<br>
<br>
At the start of the head.S code es,ds,fs,gs were all intialized to __KERNEL_DS, which is defined in /usr/src/linux/include/asm/segment.h as 0x18. This is the offset from the goobal descriptor table gdtwhich was setup in setup.S. The 24th byte is the start of the data segment descriptor, which has the base address = 0. Thus the moe routine is moved and<br>
starts at offset 0x1000 from __KERNEL_DS, the kernel data segment base (which is 0).<br>
The salient features of what is done by the decompress_kernel is discussed in the next section but it is worth noting that the when the decompressed_kernel function is invoked, space was created at the top of the stack to contain the information about the decompressed kernel. The decompressed kernel if big may be in the high buffer and in the low buffer. After the decompressed_kernel function returns, the decompressed kernel has to be moved so that we<br>
have a contiguous decompressed kernel starting from address 0x100000. To move the decompressed kernel, the important parameters needed are the start addresses of the high buffer and low buffer, and the number of bytes in the high and low buffers. This is at the top of the stack when decompressed_kernel returns (the top of the stack was passed as an argument : struct moveparams*, and in the function the fileds of the moveparams struture was adjusted toreflect the state of the decompression.)<br>
<br>
/* in compressed../misc.c */<br>
struct moveparams {<br>
uch *low_buffer_start; ! start address of the low buffer<br>
int count; ! number of bytes in the low buffer after decompression is doneuch *high_buffer_start; ! start address of the high buffer<br>
int hcount; ! number of bytes in the high buffer aftre decompression is done<br>
};<br>
<br>
Thus when the decompressed_kernel returns, the relevant bytes are popped in the respective registers as shown below. After preparing these registers the decompressed kernel is ready to be moved and the control jumps to the moved move routine at __KERNEL_CS:0x1000. The code for setting the appropriate registers is given below:<br>
<br>
popl %esi ! discard the address, has the return value (high_load) most probably<br>
popl %esi ! low_buffer_start<br>
popl ?? ! lcount<br>
popl ?? ! high_buffer_count<br>
popl ?? ! hcount<br>
movl %0x100000,??<br>
cli ! disable interrutps when the decompressed kernel is being moved<br>
ljmp $(__KERNEL_CS), $0x1000 ! jump to the move routine which was moved to low memory, 0x1000<br>
<br>
The move_routine_start basically has two parts, first it moves the part of the decompressed kernel in the low buffer, then it moves (if required) the high buffer contents. It should be noted that the ecx has been intialized to the number of bytes in the low end buffer, and the destination index register di has been intialized to 0x100000.<br>
move_routine_start:<br>
<br>
rep ! repeat, it stops repeating when ecx == 0<br>
movsb ! the movsb instruction repeats till ecx is 0. In each loop byte is transferred from ds:esi to es:edi! In each loop the edi and the esi are incremented and ecx is decremented<br>
! when the low end buffer has been moved the value of di is not changed and the next pasrt of the code! uses it to transfer the bytes from the high buffer<br>
movl ??,%esi ! esi now has the offset corresponding to the start of the high buffer<br>
movl ??,?? ! ecx is now intialized to the number of bytes in the high buffer<br>
rep<br>
movsb ! moves all the bytes in the high buffer, and doesn’t move at all if hcount was zero (if it was determined, in! close_output_buffer_if_we_run_high that the high buffer need not be moveddown )<br>
xorl ??,??<br>
mov $0x90000, %esp ! stack pointer is adjusted, most probably to be used by the kernel in the intialization<br>
ljmp $(__KERNEL_CS), $0x100000 ! jump to __KERNEL_CS:0X100000, where the kernel code starts<br>
move_routine_end:At the end of the this the control goes to the kernel code segment.<br>
<br>
<br>
linux Assembly code taken from head.S and setup.S<br>
Comment code added by us<br>
<br>
<br>
<br>
<br>
[目录]<br>
<br>
--------------------------------------------------------------------------------<br>
<br>
<br>
head.S<br>
<br>
因为setup.S最后的为一条转跳指令,跳到内核第一条指令并开始执行。指令中指向的是内存中的绝对地址,我们无法依此判断转跳到了head.S。但是我们可以通过Makefile简单的确定head.S位于内核的前端。<br>
在arch/i386 的 Makefile 中定义了<br>
HEAD := arch/i386/kernel/head.o<br>
<br>
而在linux总的Makefile中由这样的语句<br>
include arch/$(ARCH)/Makefile<br>
说明HEAD定义在该文件中有效<br>
<br>
然后由如下语句:<br>
<br>
vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs<br>
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o <br>
$(ARCHIVES) <br>
$(FILESYSTEMS) <br>
$(DRIVERS) <br>
$(LIBS) -o vmlinux<br>
$(NM) vmlinux | grep -v '(compiled)|(.o$$)|( a )' | sort &gt; System.map<br>
<br>
从这个依赖关系我们可以获得大量的信息<br>
<br>
1&gt;$(HEAD)即head.o的确第一个被连接到核心中<br>
<br>
2&gt;所有内核中支持的文件系统全部编译到$(FILESYSTEMS)即fs/filesystems.a中<br>
所有内核中支持的网络协议全部编译到net.a中<br>
所有内核中支持的SCSI驱动全部编译到scsi.a中<br>
...................<br>
原来内核也不过是一堆库文件和目标文件的集合罢了,有兴趣对内核减肥的同学,<br>
可以好好比较一下看究竟是那个部分占用了空间。<br>
<br>
3&gt;System.map中包含了所有的内核输出的函数,我们在编写内核模块的时候<br>
可以调用的系统函数大概就这些了。<br>
<br>
<br>
好了,消除了心中的疑问,我们可以仔细分析head.s了。<br>
<br>
Head.S分析<br>
<br>
1 首先将ds,es,fs,gs指向系统数据段KERNEL_DS<br>
KERNEL_DS 在asm/segment.h中定义,表示全局描述符表中<br>
中的第三项。<br>
注意:该此时生效的全局描述符表并不是在head.s中定义的<br>
而仍然是在setup.S中定义的。<br>
<br>
2 数据段全部清空。<br>
<br>
3 setup_idt为一段子程序,将中断向量表全部指向ignore_int函数<br>
该函数打印出:unknown interrupt<br>
当然这样的中断处理函数什么也干不了。<br>
<br>
4 察看数据线A20是否有效,否则循环等待。<br>
地址线A20是x86的历史遗留问题,决定是否能访问1M以上内存。<br>
<br>
5 拷贝启动参数到0x5000页的前半页,而将setup.s取出的bios参数<br>
放到后半页。<br>
<br>
6 检查CPU类型<br>
@#$#%$^*@^?(^%#$%!#!@?谁知道干了什么?<br>
<br>
7 初始化页表,只初始化最初几页。<br>
<br>
1&gt;将swapper_pg_dir(0x2000)和pg0(0x3000)清空<br>
swapper_pg_dir作为整个系统的页目录<br>
<br>
2&gt;将pg0作为第一个页表,将其地址赋到swapper_pg_dir的第一个32<br>
位字中。<br>
<br>
3&gt;同时将该页表项也赋给swapper_pg_dir的第3072个入口,表示虚拟地址<br>
0xc0000000也指向pg0。<br>
<br>
4&gt;将pg0这个页表填满指向内存前4M<br>
<br>
5&gt;进入分页方式<br>
注意:以前虽然在在保护模式但没有启用分页。<br>
<br>
--------------------<br>
| swapper_pg_dir | -----------<br>
| |-------| pg0 |----------内存前4M<br>
| | -----------<br>
| |<br>
--------------------<br>
8 装入新的gdt和ldt表。<br>
<br>
9 刷新段寄存器ds,es,fs,gs<br>
<br>
10 使用系统堆栈,即预留的0x6000页面<br>
<br>
11 执行start_kernel函数,这个函数是第一个C编制的<br>
函数,内核又有了一个新的开始。<br>
<br>
<br>
<br>
<br>
<br>
[目录]<br>
<br>
--------------------------------------------------------------------------------<br>
<br>
<br>
compressed../misc.c<br>
<br>
compressed../misc.c<br>

⌨️ 快捷键说明

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