📄 1120.html
字号:
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).<br>
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.");<br>
if (high_loaded) close_output_buffer_if_we_run_high(mv);<br>
return high_loaded;<br>
}<br>
<br>
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.<br>
<br>
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.");<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.");<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>
}<br>
<br>
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:<br>
<br>
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.<br>
<br>
linux code taken from misc.c<br>
Comment code added by us<br>
<br>
<br>
<br>
<br>
<br>
[目录]<br>
<br>
--------------------------------------------------------------------------------<br>
<br>
<br>
内核解压<br>
<br>
概述<br>
----<br>
1) linux的初始内核映象以gzip压缩文件的格式存放在zImage或bzImage之中, 内核的自举代码将它解压到1M内存开始处. 在内核初始化时, 如果加载了压缩的initrd映象, 内核会将它解压到内存盘中, 这两处解压过程都使用了lib/inflate.c文件.<br>
<br>
2) inflate.c是从gzip源程序中分离出来的, 包含了一些对全局数据的直接引用, 在使用时需要直接嵌入到代码中. gzip压缩文件时总是在前32K字节的范围内寻找重复的字符串进行编码, 在解压时需要一个至少为32K字节的解压缓冲区, 它定义为window[WSIZE].inflate.c使用get_byte()读取输入文件, 它被定义成宏来提高效率. 输入缓冲区指针必须定义为inptr, inflate.c中对之有减量操作. inflate.c调用flush_window()来输出window缓冲区中的解压出的字节串, 每次输出长度用outcnt变量表示. 在flush_window()中, 还必须对输出字节串计算CRC并且刷新crc变量. 在调用gunzip()开始解压之前, 调用makecrc()初始化CRC计算表. 最后gunzip()返回0表示解压成功.<br>
<br>
<br>
3) zImage或bzImage由16位引导代码和32位内核自解压映象两个部分组成. 对于zImage, 内核自解压映象被加载到物理地址0x1000, 内核被解压到1M的部位. 对于bzImage, 内核自解压映象被加载到1M开始的地方, 内核被解压为两个片段, 一个起始于物理地址0x2000-0x90000,另一个起始于高端解压映象之后, 离1M开始处不小于低端片段最大长度的区域. 解压完成后,这两个片段被合并到1M的起始位置.<br>
<br>
<br>
解压根内存盘映象文件的代码<br>
--------------------------<br>
<br>
; drivers/block/rd.c<br>
#ifdef BUILD_CRAMDISK<br>
<br>
/*<br>
* gzip declarations<br>
*/<br>
<br>
#define OF(args) args ; 用于函数原型声明的宏<br>
#ifndef memzero<br>
#define memzero(s, n) memset ((s), 0, (n))<br>
#endif<br>
typedef unsigned char uch; 定义inflate.c所使用的3种数据类型<br>
typedef unsigned short ush;<br>
typedef unsigned long ulg;<br>
#define INBUFSIZ 4096 用户输入缓冲区尺寸<br>
#define WSIZE 0x8000 /* window size--must be a power of two, and */<br>
/* at least 32K for zip's deflate method */<br>
<br>
static uch *inbuf; 用户输入缓冲区,与inflate.c无关<br>
static uch *window; 解压窗口<br>
static unsigned insize; /* valid bytes in inbuf */<br>
static unsigned inptr; /* index of next byte to be processed in inbuf */<br>
static unsigned outcnt; /* bytes in output buffer */<br>
static int exit_code;<br>
static long bytes_out; 总解压输出长度,与inflate.c无关<br>
static struct file *crd_infp, *crd_outfp;<br>
<br>
#define get_byte() (inptr<br>
/* Diagnostic functions (stubbed out) */ 一些调试宏<br>
#define Assert(cond,msg)<br>
#define Trace(x)<br>
#define Tracev(x)<br>
#define Tracevv(x)<br>
#define Tracec(c,x)<br>
#define Tracecv(c,x)<br>
<br>
#define STATIC static<br>
<br>
static int fill_inbuf(void);<br>
static void flush_window(void);<br>
static void *malloc(int size);<br>
static void free(void *where);<br>
static void error(char *m);<br>
static void gzip_mark(void **);<br>
static void gzip_release(void **);<br>
<br>
#include "../../lib/inflate.c"<br>
<br>
static void __init *malloc(int size)<br>
{<br>
return kmalloc(size, GFP_KERNEL);<br>
}<br>
<br>
static void __init free(void *where)<br>
{<br>
kfree(where);<br>
}<br>
<br>
static void __init gzip_mark(void **ptr)<br>
{<br>
; 读取用户一个标记<br>
}<br>
<br>
static void __init gzip_release(void **ptr)<br>
{<br>
; 归还用户标记<br>
}<br>
<br>
/* ===========================================================================<br>
* Fill the input buffer. This is called only when the buffer is empty<br>
* and at least one byte is really needed.<br>
*/<br>
<br>
static int __init fill_inbuf(void) 填充输入缓冲区<br>
{<br>
if (exit_code) return -1;<br>
insize = crd_infp->f_op->read(crd_infp, inbuf, INBUFSIZ,<br>
if (insize == 0) return -1;<br>
inptr = 1;<br>
return inbuf[0];<br>
}<br>
<br>
/* ===========================================================================<br>
* Write the output window window[0..outcnt-1] and update crc and bytes_out.<br>
* (Used for the decompressed data only.)<br>
*/<br>
<br>
static void __init flush_window(void) 输出window缓冲区中outcnt个字节串<br>
{<br>
ulg c = crc; /* temporary variable */<br>
unsigned n;<br>
uch *in, ch;<br>
<br>
crd_outfp->f_op->write(crd_outfp, window, outcnt,<br>
in = window;<br>
for (n = 0; n ch = *in++;<br>
c = crc_32_tab[((int)c ^ ch) 0xff] ^ (c >> icon_cool.gif; 计算输出串的CRC<br>
}<br>
crc = c;<br>
bytes_out += (ulg)outcnt; 刷新总字节数<br>
outcnt = 0;<br>
}<br>
<br>
static void __init error(char *x) 解压出错调用的函数<br>
{<br>
printk(KERN_ERR "%s", x);<br>
exit_code = 1;<br>
}<br>
<br>
<br>
static int __init<br>
crd_load(struct file * fp, struct file *outfp)<br>
{<br>
int result;<br>
<br>
insize = 0; /* valid bytes in inbuf */<br>
inptr = 0; /* index of next byte to be processed in inbuf */<br>
outcnt = 0; /* bytes in output buffer */<br>
exit_code = 0;<br>
bytes_out = 0;<br>
crc = (ulg)0xffffffffL; /* shift register contents */<br>
<br>
crd_infp = fp;<br>
crd_outfp = outfp;<br>
inbuf = kmalloc(INBUFSIZ, GFP_KERNEL);<br>
if (inbuf == 0) {<br>
printk(KERN_ERR "RAMDISK: Couldn't allocate gzip buffer");<br>
return -1;<br>
}<br>
window = kmalloc(WSIZE, GFP_KERNEL);<br>
if (window == 0) {<br>
printk(KERN_ERR "RAMDISK: Couldn't allocate gzip window");<br>
kfree(inbuf);<br>
return -1;<br>
}<br>
makecrc();<br>
result = gunzip();<br>
kfree(inbuf);<br>
kfree(window);<br>
return result;<br>
}<br>
<br>
#endif /* BUILD_CRAMDISK */<br>
<br>
32位内核自解压代码<br>
------------------<br>
<br>
; arch/i386/boot/compressed/head.S<br>
<br>
.text<br>
#include ·<br>
#include<br>
.globl startup_32 对于zImage该入口地址为0x1000; 对于bzImage为0x101000<br>
startup_32:<br>
cld<br>
cli<br>
movl $(__KERNEL_DS),%eax<br>
movl %eax,%ds<br>
movl %eax,%es<br>
movl %eax,%fs<br>
movl %eax,%gs<br>
<br>
lss SYMBOL_NAME(stack_start),%esp # 自解压代码的堆栈为misc.c中定义的16K字节的数组<br>
xorl %eax,%eax<br>
1: incl %eax # check that A20 really IS enabled<br>
movl %eax,0x000000 # loop forever if it isn't<br>
cmpl %eax,0x100000<br>
je 1b<br>
<br>
/*<br>
* Initialize eflags. Some BIOS's leave bits like NT set. This would<br>
* confuse the debugger if this code is traced.<br>
* XXX - best to initialize before switching to protected mode.<br>
*/<br>
pushl $0<br>
popfl<br>
/*<br>
* Clear BSS 清除解压程序的BSS段<br>
*/<br>
xorl %eax,%eax<br>
movl $ SYMBOL_NAME(_edata),%edi<br>
movl $ SYMBOL_NAME(_end),%ecx<br>
subl %edi,%ecx<br>
cld<br>
rep<br>
stosb<br>
/*<br>
* Do the decompression, and jump to the new kernel..<br>
*/<br>
subl $16,%esp # place for structure on the stack<br>
movl %esp,%eax<br>
pushl %esi # real mode pointer as second arg<br>
pushl %eax # address of structure as first arg<br>
call SYMBOL_NAME(decompress_kernel)<br>
orl %eax,%eax #
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -