📄 1.html
字号:
#LOADED_HIGH<br>jz do_move0 ! a normal low loaded zImage is moved<br>jmp end_move ! skip move<p>The interrupt and global descriptors are initialized:<p>lidt idt_48 ! load idt wit 0,0<br>lgdt gdt_48 ! load gdt with whatever appropriate<p>After enabling A20 and reprogramming the interrupts, it is ready to set the PE bit:<p>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<p>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.<p>compressed/head.S<p>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.<p>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<p>subl $16,%esp<br>pushl %esp<br>call SYMBOL_NAME(decompress_kernel)<br>orl ??,??<br>jnz 3f<p>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.<p>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<p>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.<p> 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.)<p>/* 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>};<p>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:<p>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<p>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:<p>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.<p><br>Linux Assembly code taken from head.S and setup.S<br>Comment code added by us<p><p><p><center><A HREF="#Content">[目录]</A></center><hr><br><A NAME="I358" ID="I358"></A><center><b><font size=+2>head.S</font></b></center><br> 因为setup.S最后的为一条转跳指令,跳到内核第一条指令并开始执行。指令中指向的是内存中的绝对地址,我们无法依此判断转跳到了head.S。但是我们可以通过Makefile简单的确定head.S位于内核的前端。<p>在arch/i386 的 Makefile 中定义了<br> HEAD := arch/i386/kernel/head.o<p>而在linux总的Makefile中由这样的语句<br> include arch/$(ARCH)/Makefile<br>说明HEAD定义在该文件中有效<p> 然后由如下语句:<p>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 > System.map<p>从这个依赖关系我们可以获得大量的信息<p>1>$(HEAD)即head.o的确第一个被连接到核心中<p>2>所有内核中支持的文件系统全部编译到$(FILESYSTEMS)即fs/filesystems.a中<br> 所有内核中支持的网络协议全部编译到net.a中<br> 所有内核中支持的SCSI驱动全部编译到scsi.a中<br> ...................<br> 原来内核也不过是一堆库文件和目标文件的集合罢了,有兴趣对内核减肥的同学,<br> 可以好好比较一下看究竟是那个部分占用了空间。<p>3>System.map中包含了所有的内核输出的函数,我们在编写内核模块的时候<br> 可以调用的系统函数大概就这些了。<p><br>好了,消除了心中的疑问,我们可以仔细分析head.s了。<p>Head.S分析<p>1 首先将ds,es,fs,gs指向系统数据段KERNEL_DS<br> KERNEL_DS 在asm/segment.h中定义,表示全局描述符表中<br> 中的第三项。<br> 注意:该此时生效的全局描述符表并不是在head.s中定义的<br> 而仍然是在setup.S中定义的。<p>2 数据段全部清空。<p>3 setup_idt为一段子程序,将中断向量表全部指向ignore_int函数<br> 该函数打印出:unknown interrupt<br> 当然这样的中断处理函数什么也干不了。<p>4 察看数据线A20是否有效,否则循环等待。<br> 地址线A20是x86的历史遗留问题,决定是否能访问1M以上内存。<p>5 拷贝启动参数到0x5000页的前半页,而将setup.s取出的bios参数<br> 放到后半页。<p>6 检查CPU类型<br> @#$#%$^*@^?(^%#$%!#!@?谁知道干了什么?<p>7 初始化页表,只初始化最初几页。<p> 1>将swapper_pg_dir(0x2000)和pg0(0x3000)清空<br> swapper_pg_dir作为整个系统的页目录<p> 2>将pg0作为第一个页表,将其地址赋到swapper_pg_dir的第一个32<br> 位字中。<p> 3>同时将该页表项也赋给swapper_pg_dir的第3072个入口,表示虚拟地址<br> 0xc0000000也指向pg0。<p> 4>将pg0这个页表填满指向内存前4M<p> 5>进入分页方式<br> 注意:以前虽然在在保护模式但没有启用分页。<p> --------------------<br> | swapper_pg_dir | -----------<br> | |-------| pg0 |----------内存前4M<br> | | -----------<br> | |<br> --------------------<br>8 装入新的gdt和ldt表。<p>9 刷新段寄存器ds,es,fs,gs<p>10 使用系统堆栈,即预留的0x6000页面<p>11 执行start_kernel函数,这个函数是第一个C编制的<br> 函数,内核又有了一个新的开始。<p><p><br><center><A HREF="#Content">[目录]</A></center><hr><br><A NAME="I359" ID="I359"></A><center><b><font size=+2>compressed/misc.c</font></b></center><br>compressed/misc.c<br>The differences in decompressing big and small kernels.<br>http://www.vuse.vanderbilt.edu/~knopfdg/documentation/hw3_part3.htm<br>The function decompressed_kernel is invoked from head.S and a parameter to the top of the stack is passed to store the results of the decompression namely, the start addresses of the high and the low buffers which contain the decompressed kernel and the numebr of bytes in each buffer (hcount and lcount).<p>int decompress_kernel(struct moveparams *mv)<br>{<br>if (SCREEN_INFO.orig_video_mode == 7) {<br>vidmem = (char *) 0xb0000;<br>vidport = 0x3b4;<br>} else {<br>vidmem = (char *) 0xb8000;<br>vidport = 0x3d4;<br>}<br>lines = SCREEN_INFO.orig_video_lines;<br>cols = SCREEN_INFO.orig_video_cols;<br>if (free_mem_ptr < 0x100000) setup_normal_output_buffer(); // Call if smallkernel<br>else setup_output_buffer_if_we_run_high(mv); // Call if big kernel<br>makecrc();<br>puts("Uncompressing Linux... ");<br>gunzip();<br>puts("Ok, booting the kernel.\n");<br>if (high_loaded) close_output_buffer_if_we_run_high(mv);<br>return high_loaded;<br>}<p>The first place where a distinction is made is when the buffers are to be setup for the decmpression routine gunzip(). Free_mem_ptr, is loaded with the value of the address of the extern variabe end. The variable end marks the end of the compressed kernel. If the free_mem-ptr is less than the 0x100000,then a high buffer has to be setup. Thus the function setup_output_buffer_if_we_run_high is called and the pointer to the top of the moveparams structure is passed so that when the buffers are setup, the start addresses fields are updated in moveparams structure. It is also checked to see if the high buffer needs to be moved down after decompression and this is reflected by the hcount which is 0 if we need not move the high buffer down.<p>void setup_output_buffer_if_we_run_high(struct moveparams *mv)<br>{<br>high_buffer_start = (uch *)(((ulg)&end) HEAP_SIZE);<br>//the high buffer start address is at the end HEAP_SIZE<br>#ifdef STANDARD_MEMORY_BIOS_CALL<br>if (EXT_MEM_K < (3*1024)) error("Less than 4MB of memory.\n");<br>#else<br>if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3*1024)) error("Less<br>than 4MB of memory.\n");<br>#endif<br>mv->low_buffer_start = output_data = (char *)LOW_BUFFER_START;<br>//the low buffer start address is at 0x2000 and it extends till 0x90000.<br>high_loaded = 1; //high_loaded is set to 1, this is returned by decompressed_kernel<br>free_mem_end_ptr = (long)high_buffer_start;<br>// free_mem_end_ptr points to the same address as te high_buffer_start<br>// the code below finds out if the high buffer needs to be moved after decompression<br>// if the size if the low buffer is > the size of the compressed kernel and the HEAP_SIZE<br>// then the high_buffer_start has to be shifted up so that when the decompression starts it doesn’t<br>// overwrite the compressed kernel data. Thus when the high_buffer_start islow then it is shifted<br>// up to exactly match the end of the compressed kernel and the HEAP_SIZE. The hcount filed is<br>// is set to 0 as the high buffer need not be moved down. Otherwise if the high_buffer_start is too<br>// high then the hcount is non zero and while closing the buffers the appropriate number of bytes<br>// in the high buffer is asigned to the filed hcount. Since the start address of the high buffer is<br>// known the bytes could be moved down<br>if ( (0x100000 LOW_BUFFER_SIZE) > ((ulg)high_buffer_start)) {<br>high_buffer_start = (uch *)(0x100000 LOW_BUFFER_SIZE);<br>mv->hcount = 0; /* say: we need not to move high_buffer */<br>}<br>else mv->hcount = -1;<br>mv->high_buffer_start = high_buffer_start;<br>// finally the high_buffer_start field is set to the varaible high_buffer_start<br>}<p>After the buffers are set gunzip() is invoked which decompresses the kernel Upon return, bytes_out has the number of bytes in the decompressed kernel.Finally close_output_buffer_if_we_run_high is invoked if high_loaded is non zero:<p>void close_output_buffer_if_we_run_high(struct moveparams *mv)<br>{<br>mv->lcount = bytes_out;<br>// if the all of decompressed kernel is in low buffer, lcount = bytes_out<br>if (bytes_out > LOW_BUFFER_SIZE) {<br>// if there is a part of the decompressed kernel in the high buffer, the lcount filed is set to<br>// the size of the low buffer and the hcount field contains the rest of the bytes<br>mv->lcount = LOW_BUFFER_SIZE;<br>if (mv->hcount) mv->hcount = bytes_out - LOW_BUFFER_SIZE;<br>// if the hcount field is non zero (made in setup_output_buffer_if_we_run_high)<br>// then the high buffer has to be moved doen and the number of bytes in the high buffer is<br>// in hcount<br>}<br>else mv->hcount = 0; // all the data is in the high buffer<br>}<br>Thus at the end of the the decompressed_kernel function the top of the stack has the addresses of the buffers and their sizes which is popped and the appropriate registers set for the move routine to move the entire kernel. After the move by the move_routine the kernel resides at 0x100000. If a small kernel is being decompressed then the setup_normal_output_buffer() is invoked from decompressed_kernel, which just initializes output_data to 0x100000 where the decompressed kernel would lie. The variable high_load is still 0 as setup_output_buffer_if_we_run_high() is not invoked. Decompression is done starting at address 0x100000. As high_load is 0, when decompressed_kernel returns in head.S, a zero is there in the eax. Thus the control jumps directly to 0x100000. Since the decompressed kernel lies there directly and the move routine need not be called.<p>Linux code taken from misc.c<br>Comment code added by us<p><p><br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -