Linux的初始内核自解压分析

(2009-03-27 19:46:46)

标签:

it

Linux的初始内核解压

2007-09-19 15:02 来源:论坛整理 作者:lucian_yao 【网友评论0条 我要说两句】 【字号:大 中

小】

关于Linux的初始内核解压比较详细的资料 概述

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

#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

static uch *inbuf; 用户输入缓冲区,与inflate.c无关

static uch *window; 解压窗口

static unsigned insize;

static unsigned inptr;

static unsigned outcnt;

static int exit_code;

static long bytes_out; 总解压输出长度,与inflate.c无关

static struct file *crd_infp, *crd_outfp;

#define get_byte() (inptr

一些调试宏

#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)

{

; 归还用户标记

}

static int __init fill_inbuf(void) 填充输入缓冲区

{

if (exit_code) return -1;

insize =

crd_infp->f_op->read(crd_infp, inbuf,

INBUFSIZ,

if (insize == 0) return -1;

inptr = 1;

return inbuf[0];

}

static void __init flush_window(void)

输出window缓冲区中outcnt个字节串

{

ulg c = crc;

unsigned n;

uch *in, ch;

crd_outfp->f_op->write(crd_outfp,

window, outcnt,

in = window;

for (n = 0; 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;

inptr = 0;

outcnt = 0;

exit_code = 0;

bytes_out = 0;

crc = (ulg)0xffffffffL;

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

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

pushl $0

popfl

xorl %eax,%eax

movl $ SYMBOL_NAME(_edata),%edi

movl $ SYMBOL_NAME(_end),%ecx

subl %edi,%ecx

cld

rep

stosb

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

3:

movl $move_routine_start,%esi

movl $0x1000,%edi

movl $move_routine_end,%ecx

subl %esi,%ecx

addl $3,%ecx

shrl $2,%ecx # 按字取整

cld

rep

movsl # 将内核片断合并代码复制到0x1000区域, 内核的片段起始为0x2000

popl %esi # discard the address

popl %ebx # real mode pointer

popl %esi # low_buffer_start 内核低端片段的起始地址

popl %ecx # lcount 内核低端片段的字节数量

popl %edx # high_buffer_start 内核高端片段的起始地址

popl %eax # hcount 内核高端片段的字节数量

movl $0x100000,%edi 内核合并的起始地址

cli # make sure we don't get interrupted

ljmp $(__KERNEL_CS), $0x1000 # and jump to the move routine

move_routine_start:

movl %ecx,%ebp

shrl $2,%ecx

rep

movsl # 按字拷贝第1个片段

movl %ebp,%ecx

andl $3,%ecx

rep

movsb # 传送不完全字

movl %edx,%esi

movl %eax,%ecx # NOTE: rep movsb won't move if %ecx == 0

addl $3,%ecx

shrl $2,%ecx # 按字对齐

rep

movsl # 按字拷贝第2个片段

movl %ebx,%esi # Restore setup pointer

xorl %ebx,%ebx

ljmp $(__KERNEL_CS), $0x100000 # 运行start_kernel

move_routine_end:

; arch/i386/boot/compressed/misc.c

#define OF(args) args

#define STATIC static

#undef memset

#undef memcpy

#define memzero(s, n) memset ((s), 0, (n))

ypedef unsigned char uch;

typedef unsigned short ush;

typedef unsigned long ulg;

#define WSIZE 0x8000

static uch *inbuf;

static uch window[WSIZE];

static unsigned insize = 0;

static unsigned inptr = 0;

static unsigned outcnt = 0;

#define ASCII_FLAG 0x01

#define CONTINUATION 0x02

#define EXTRA_FIELD 0x04

#define ORIG_NAME 0x08

#define COMMENT 0x10

#define ENCRYPTED 0x20

#define RESERVED 0xC0

#define get_byte() (inptr

#ifdef DEBUG

# define Assert(cond,msg) {if(!(cond)) error(msg);}

# define Trace(x) fprintf x

# define Tracev(x) {if (verbose) fprintf x ;}

# define Tracevv(x) {if (verbose>1) fprintf x

;}

# define Tracec(c,x) {if (verbose (c)) fprintf x ;}

# define Tracecv(c,x) {if (verbose>1 (c)) fprintf

x ;}

#else

# define Assert(cond,msg)

# define Trace(x)

# define Tracev(x)

# define Tracevv(x)

# define Tracec(c,x)

# define Tracecv(c,x)

#endif

static int fill_inbuf(void);

static void flush_window(void);

static void error(char *m);

static void gzip_mark(void **);

static void gzip_release(void **);

static unsigned char *real_mode;

#define EXT_MEM_K (*(unsigned short *)(real_mode + 0x2))

#ifndef STANDARD_MEMORY_BIOS_CALL

#define ALT_MEM_K (*(unsigned long *)(real_mode + 0x1e0))

#endif

#define SCREEN_INFO (*(struct screen_info *)(real_mode+0))

extern char input_data[];

extern int input_len;

static long bytes_out = 0;

static uch *output_data;

static unsigned long output_ptr = 0;

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 **);

static void puts(const char *);

extern int end;

static long free_mem_ptr = (long)

static long free_mem_end_ptr;

#define INPLACE_MOVE_ROUTINE 0x1000 内核片段合并代码的运行地址

#define LOW_BUFFER_START 0x2000 内核低端解压片段的起始地址

#define LOW_BUFFER_MAX 0x90000 内核低端解压片段的终止地址

#define HEAP_SIZE 0x3000 为解压低码保留的堆的尺寸,堆起始于BSS的结束

static unsigned int low_buffer_end, low_buffer_size;

static int high_loaded =0;

static uch *high_buffer_start ;

static char *vidmem = (char *)0xb8000;

static int vidport;

static int lines, cols;

#include "../../../../lib/inflate.c"

static void *malloc(int size)

{

void *p;

if (size if (free_mem_ptr

free_mem_ptr = (free_mem_ptr + 3) ~3;

p = (void *)free_mem_ptr;

free_mem_ptr += size;

if (free_mem_ptr >= free_mem_end_ptr)

error("\nOut of memory\n");

return p;

}

static void free(void *where)

{

}

static void gzip_mark(void **ptr)

{

*ptr = (void *) free_mem_ptr;

}

static void gzip_release(void **ptr)

{

free_mem_ptr = (long) *ptr;

}

static void scroll(void)

{

int i;

memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2

);

for ( i = ( lines - 1 ) * cols * 2; i vidmem[ i ] = ' ';

}

static void puts(const char *s)

{

int x,y,pos;

char c;

x = SCREEN_INFO.orig_x;

y = SCREEN_INFO.orig_y;

while ( ( c = *s++ ) != '\0' ) {

if ( c == '\n' ) {

x = 0;

if ( ++y >= lines ) {

scroll();

y--;

}

} else {

vidmem [ ( x + cols * y ) * 2 ] = c;

if ( ++x >= cols ) {

x = 0;

if ( ++y >= lines ) {

scroll();

y--;

}

}

}

}

SCREEN_INFO.orig_x = x;

SCREEN_INFO.orig_y = y;

pos = (x + cols * y) * 2;

outb_p(14, vidport);

outb_p(0xff (pos >> 9),

vidport+1);

outb_p(15, vidport);

outb_p(0xff (pos >> 1),

vidport+1);

}

void* memset(void* s, int c, size_t n)

{

int i;

char *ss = (char*)s;

for (i=0;i return s;

}

void* memcpy(void* __dest, __const void* __src,

size_t __n)

{

int i;

char *d = (char *)__dest, *s = (char *)__src;

for (i=0;i return __dest;

}

static int fill_inbuf(void)

{

if (insize != 0) {

error("ran out of input data\n");

}

inbuf = input_data;

insize = input_len;

inptr = 1;

return inbuf[0];

}

static void flush_window_low(void)

{

ulg c = crc;

unsigned n;

uch *in, *out, ch;

in = window;

out =

for (n = 0; n ch = *out++ = *in++;

c = crc_32_tab[((int)c ^ ch) 0xff] ^ (c

>> 8);

}

crc = c;

bytes_out += (ulg)outcnt;

output_ptr += (ulg)outcnt;

outcnt = 0;

}

static void flush_window_high(void)

{

ulg c = crc;

unsigned n;

uch *in, ch;

in = window;

for (n = 0; n ch = *output_data++ = *in++;

if ((ulg)output_data == low_buffer_end)

output_data=high_buffer_start;

c = crc_32_tab[((int)c ^ ch) 0xff] ^ (c

>> 8);

}

crc = c;

bytes_out += (ulg)outcnt;

outcnt = 0;

}

static void flush_window(void)

{

if (high_loaded) flush_window_high();

else flush_window_low();

}

static void error(char *x)

{

puts("\n\n");

puts(x);

puts("\n\n -- System halted");

while(1);

}

#define STACK_SIZE (4096)

long user_stack [STACK_SIZE];

struct {

long * a;

short b;

} stack_start = { user_stack [STACK_SIZE] , __KERNEL_DS };

void setup_normal_output_buffer(void) 对于zImage, 直接解压到1M

{

#ifdef STANDARD_MEMORY_BIOS_CALL

if (EXT_MEM_K #else

if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K :

EXT_MEM_K) #endif

output_data = (char *)0x100000;

free_mem_end_ptr = (long)real_mode;

}

struct moveparams {

uch *low_buffer_start; int lcount;

uch *high_buffer_start; int hcount;

};

void setup_output_buffer_if_we_run_high(struct moveparams

*mv)

{

high_buffer_start = (uch *)(((ulg) + HEAP_SIZE);

内核高端片段的最小起始地址

#ifdef STANDARD_MEMORY_BIOS_CALL

if (EXT_MEM_K #else

if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K :

EXT_MEM_K) #endif

mv->low_buffer_start = output_data = (char

*)LOW_BUFFER_START;

low_buffer_end = ((unsigned int)real_mode >

LOW_BUFFER_MAX

? LOW_BUFFER_MAX : (unsigned int)real_mode) ~0xfff;

low_buffer_size = low_buffer_end - LOW_BUFFER_START;

high_loaded = 1;

free_mem_end_ptr = (long)high_buffer_start;

if ( (0x100000 + low_buffer_size) >

((ulg)high_buffer_start)) {

; 如果高端片段的最小起始地址小于它实际应加载的地址,则将它置为实际地址,

; 这样高端片段就无需再次移动了,否则它要向前移动

high_buffer_start = (uch *)(0x100000 + low_buffer_size);

mv->hcount = 0;

}

else mv->hcount = -1; 待定

mv->high_buffer_start = high_buffer_start;

}

void close_output_buffer_if_we_run_high(struct moveparams

*mv)

{

if (bytes_out > low_buffer_size) {

mv->lcount = low_buffer_size;

if (mv->hcount)

mv->hcount = bytes_out - low_buffer_size;

求出高端片段的字节数

} else { 如果解压后内核只有低端的一个片段

mv->lcount = bytes_out;

mv->hcount = 0;

}

}

int decompress_kernel(struct moveparams *mv, void *rmode)

{

real_mode = rmode;

if (SCREEN_INFO.orig_video_mode == 7) {

vidmem = (char *) 0xb0000;

vidport = 0x3b4;

} else {

vidmem = (char *) 0xb8000;

vidport = 0x3d4;

}

lines = SCREEN_INFO.orig_video_lines;

cols = SCREEN_INFO.orig_video_cols;

if (free_mem_ptr else

setup_output_buffer_if_we_run_high(mv);

makecrc();

puts("Uncompressing Linux... ");

gunzip();

puts("Ok, booting the kernel.\n");

if (high_loaded) close_output_buffer_if_we_run_high(mv);

return high_loaded;

}

分享:

喜欢

0

赠金笔

加载中,请稍候......

评论加载中,请稍候...

发评论

登录名: 密码: 找回密码 注册记住登录状态

昵   称:

发评论

以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

linux内核自解压,Linux的初始内核自解压分析相关推荐

  1. 【内核】linux内核启动流程详细分析【转】

    转自:http://www.cnblogs.com/lcw/p/3337937.html Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件 ...

  2. 【内核】linux内核启动流程详细分析

    Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用 ...

  3. 编译Linux内核没有zImage,Linux 编译系统的简单介绍与内核编译安装

    这里不只是讲怎样编译.安装Linux内核的,更主要的是介绍内核的编译系统和各个重要的文件.最后还利用学到的编译.安装Linux内核去修改Linux的01调度变成随机调度.如果你只是需要编译.安装内核的 ...

  4. linux怎样查看内核参数,Linux 实例如何查看和修改 Linux 实例内核参数?

    <操作系统>课程设计报告课程设计题目:操作系统课程设计 设计时间:2016/1/10一. 课程设计目的与要求需要完成的内容:(1) 安装虚拟机:Vmware.Vmware palyer ( ...

  5. linux禁止内核抢占,Linux内核态抢占机制分析

    [51CTO晃荡]8.26 带你深度懂得清华大年夜学.搜狗基于算法的IT运维实践与摸索 本文起首介绍非抢占式内核(Non-Preemptive Kernel)和可抢占式内核(Preemptive Ke ...

  6. linux内核 jiffies,Linux内核中的jiffies

    Linux内核中的jiffies 定义:全局变量jiffies用来记录自系统启动以来产生的节拍的总数.启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值.一秒内时钟中断的次数 ...

  7. Linux内核虚拟地址空间,-3G的由来。各个进程的虚拟内存4G,内核总在3-4G。内核的虚拟空间地址-3G,总是指向物理内存的0-1G地址,各个进程的虚拟内核共享这个物理内存

    Linux内核地址空间划分 通常 32 位 Linux 内核地址空间划分 0~3G 为用户空间,3~4G 为内核空间.64 位内核地址空间划分是不同的. Linux内核高端内存 当内核模块代码或线程访 ...

  8. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 【转】...

    原文地址:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinauni ...

  9. linux5.5内核,一条命令就可以升级到Linux 5.5内核或Linux 5.5以上版本

    升级Linux内核其实很简单,本文介绍的方法只需要一条命令,至少能够升级到Linux 5.5内核版本.当然,脚本当中要是加入新的Linux内核,你就能够升级到那个新内核,也就是说可以升级到Linux ...

最新文章

  1. smarty实例教程
  2. Mysql加锁过程详解(2)-关于mysql 幻读理解
  3. Python3 动态导入模块的两种方式
  4. nyoj-括号匹配(二)---动态规划
  5. App设计灵感之十二组精美的地图导航App设计案例
  6. matlab 判断鼠标按下,Matlab:如何通过使用回调来获取当前鼠标在点击位置
  7. php的缓存机制,PHP缓存机制
  8. mysql 屏蔽索引_mysql强制索引和禁止某个索引
  9. 【Elasticsearch】es IK分词器的安装
  10. 如何构建高效可信的持续交付能力,华为云有绝活!
  11. 10种受欢迎的前后端相关开发工具
  12. 怎样设置CCProxy
  13. Tomcat部署多个Sring Boot项目时Unable To Register MBean Exception的一种解决方法
  14. SQL零基础入门学习(十三)
  15. 分享48个Go源码,总有一款适合您
  16. xlsx怎么设置行高列宽_Excel里面如何调整行高和列宽
  17. pygame战棋游戏制作之战棋棋子设置(二)
  18. 中科合成油冲刺科创板:年营收10.7亿 拟募资8.25亿
  19. 【题解】《算法零基础100讲》(第44讲) 位运算 (位或) 入门
  20. 金百福系统一直显示连接服务器,三维力控组态软件V7.2/7.1/7.0 新版加密狗无限点全授权...

热门文章

  1. 20分钟学会CMake
  2. php word excel转pdf文件怎么打开,php office文件(word/excel/ppt)转pdf文件,pptpdf
  3. python 词云手把手_手把手教你生成炫酷的词云
  4. 【Zookeeper】windows环境下zookeeper安装
  5. docker~Dockerfile优化程序的部署
  6. Java 9 揭秘(16. 虚拟机栈遍历)
  7. 开始学习吧,《算法》:动态连通性练习
  8. 苹果新的编程语言 Swift 语言进阶(三)--基本运算和扩展运算
  9. Spring AOP动态代理-切面
  10. 【转】Android 快捷方式的创建