作者:opera 概述 ---- 1) Linux的初始內核映象以gzip壓縮文件的格式存放在zImage或bzImage之中, 內核的自舉 代碼將它解壓到1M內存開始處. 在內核初始化時, 如果加載了壓縮的initrd映象, 內核會將解壓到內存盤中, 這兩處解壓過程都使用了lib/inflate.c文件. 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表示解壓成功. 3) zImage或bzImage由16位引導代碼和32位內核自解壓映象兩個部分組成. 對於zImage, 內 核自解壓映象被加載到物理地址0x1000, 內核被解壓到1M的部位. 對於bzImage, 內核自解 壓映象被加載到1M開始的地方, 內核被解壓為兩個片段, 一個起始於物理地址0x2000-0x90000, 另一個起始於高端解壓映象之後, 離1M開始處不小於低端片段最大長度的區域. 解壓完成後, 這兩個片段被合並到1M的起始位置. 解壓根內存盤映象文件的代碼 -------------------------- 代碼: ; drivers/block/rd.c #ifdef BUILD_CRAMDISK /* * gzip declarations */ #define OF(args) args ; 用於函數原型聲明的宏 #ifndef memzero #define memzero(s, n) memset ((s), 0, (n)) #endif typedef unsigned char UCh; 定義inflate.c所使用的3種數據類型 typedef unsigned short ush; typedef unsigned long ulg; #define INBUFSIZ 4096 用戶輸入緩沖區尺寸 #define WSIZE 0x8000 /* window size--must be a power of two, and */ /* at least 32K for zip's deflate method */ static uch *inbuf; 用戶輸入緩沖區,與inflate.c無關 static uch *window; 解壓窗口 static unsigned insize; /* valid bytes in inbuf */ static unsigned inptr; /* index of next byte to be processed in inbuf */ static unsigned outcnt; /* bytes in output buffer */ static int exit_code; static long bytes_out; 總解壓輸出長度,與inflate.c無關 static struct file *crd_infp, *crd_outfp; #define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) 讀取輸入緩沖區中一個字節 /* Diagnostic functions (stubbed out) */ 一些調試宏 #define Assert(cond,msg) #define Trace(x) #define Tracev(x) #define Tracevv(x) #define Tracec(c,x) #define Tracecv(c,x) #define STATIC static static int fill_inbuf(void); static void flush_window(void); static void *malloc(int size); static void free(void *where); static void error(char *m); static void gzip_mark(void **); static void gzip_release(void **); #include "../../lib/inflate.c" static void __init *malloc(int size) { return kmalloc(size, GFP_KERNEL); } static void __init free(void *where) { kfree(where); } static void __init gzip_mark(void **ptr) { ; 讀取用戶一個標記 } static void __init gzip_release(void **ptr) { ; 歸還用戶標記 } /* =========================================================================== * Fill the input buffer. This is called only when the buffer is empty * and at least one byte is really needed. */ static int __init fill_inbuf(void) 填充輸入緩沖區 { if (exit_code) return -1; insize = crd_infp->f_op->read(crd_infp, inbuf, INBUFSIZ, &crd_infp->f_pos); if (insize == 0) return -1; inptr = 1; return inbuf[0]; } /* =========================================================================== * Write the output window window[0..outcnt-1] and update crc and bytes_out. * (Used for the decompressed data only.) */ static void __init flush_window(void) 輸出window緩沖區中outcnt個字節串 { ulg c = crc; /* temporary variable */ unsigned n; uch *in, ch; crd_outfp->f_op->write(crd_outfp, window, outcnt, &crd_outfp->f_pos); in = window; for (n = 0; n < outcnt; n++) { ch = *in++; c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); 計算輸出串的CRC } crc = c; bytes_out += (ulg)outcnt; 刷新總字節數 outcnt = 0; } static void __init error(char *x) 解壓出錯調用的函數 { printk(KERN_ERR "%s", x); exit_code = 1; } static int __init crd_load(struct file * fp, struct file *outfp) { int result; insize = 0; /* valid bytes in inbuf */ inptr = 0; /* index of next byte to be processed in inbuf */ outcnt = 0; /* bytes in output buffer */ exit_code = 0; bytes_out = 0; crc = (ulg)0xffffffffL; /* shift register contents */ crd_infp = fp; crd_outfp = outfp; inbuf = kmalloc(INBUFSIZ, GFP_KERNEL); if (inbuf == 0) { printk(KERN_ERR "RAMDISK: Couldn't allocate gzip buffer\n"); return -1; } window = kmalloc(WSIZE, GFP_KERNEL); if (window == 0) { printk(KERN_ERR "RAMDISK: Couldn't allocate gzip window\n"); kfree(inbuf); return -1; } makecrc(); result = gunzip(); kfree(inbuf); kfree(window); return result; } #endif /* BUILD_CRAMDISK */ 32位內核自解壓代碼 ------------------ ; arch/i386/boot/compressed/head.S .text #include #include .globl startup_32 對於zImage該入口地址為0x1000; 對於bzImage為0x101000 startup_32: cld cli movl $(__KERNEL_DS),%eax movl %eax,%ds movl %eax,%es movl %eax,%fs movl %eax,%gs lss SYMBOL_NAME(stack_start),%esp # 自解壓代碼的堆棧為misc.c中定義的16K字節的數組 xorl %eax,%eax 1: incl %eax # check that A20 really IS enabled movl %eax,0x000000 # loop forever if it isn't cmpl %eax,0x100000 je 1b /* * Initialize eflags. Some BIOS's leave bits like NT set. This would * confuse the debugger if this code is traced. * XXX - best to initialize before switching to protected mode. */ pushl $0 popfl /* * Clear BSS 清除解壓程序的BSS段 */ xorl %eax,%eax movl $ SYMBOL_NAME(_edata),%edi movl $ SYMBOL_NAME(_end),%ecx subl %edi,%ecx cld rep stosb /* * Do the decompression, and jump to the new kernel.. */ subl $16,%esp # place for structure on the stack movl %esp,%eax pushl %esi # real mode pointer as second arg pushl %eax # address of structure as first arg call SYMBOL_NAME(decompress_kernel) orl %eax,%eax # 如果返回非零,則表示為內核解壓為低端和高端的兩個片斷 jnz 3f popl %esi # discard address popl %esi # real mode pointer xorl %ebx,%ebx ljmp $(__KERNEL_CS), $0x100000 # 運行start_kernel /* * We come here, if we were loaded high.