当前位置:Linux教程 - 内核技术 - 内核技术 - linux内核分析----初始化

内核技术 - linux内核分析----初始化

linux内核分析----初始化
2004-04-23 15:18 pm

来自:Linux文档
现载:Www.8s8s.coM
地址:无名

参考我提供的三个文件:bootsect.txt,head.txt,setup.txt
至于x86的引导无非如下步骤:
1,cpu初始化自身,在固定位置执行一条指令。
2,这条指令条转到bios中。
3,bios找到启动设备并获取mbr,该mbr指向我们的lilo
4,bios装载并把控制权交给lilo
5,压缩内核自解压,并把控制权转交给解压内核。
简单点讲,就是cpu成为内核引导程序的引导程序的引导程序的引导程序,西西。
这时内核将跳转到start_kernel是/init/main.c的重点函数,main.c函数很多定义都是为此函数服务的,这里我简要介绍一下这个函数的初始化流程。

初始化内核:
从start_kernel函数(/init/main.c)开始系统初始化工作,好,我们首先分析这个函数:
函数开始首先:
#ifdef __SMP__
static int boot_cpu = 1;
/* "current" has been set up, we need to load it now *//*定义双处理器用*/
if (!boot_cpu)
initialize_secondary();
boot_cpu = 0;
#endif

定义双处理器。

printk(linux_banner); /*打印linux banner*/
打印内核标题信息。

开始初始化自身的部分组件(包括内存,硬件终端,调度等),我来逐个分析其中的函数:
setup_arch(&command_line, &memory_start, &memory_end);/*初始化内存*/
返回内核参数和内核可用的物理地址范围
函数原型如下:
setup_arch(char **, unsigned long *, unsigned long *);
返回内存起始地址:
memory_start = paging_init(memory_start,memory_end);
看看paging_init的定义,是初始化请求页:
paging_init(unsigned long start_mem, unsigned long end_mem)(会在以后的内存管理子系统分析时详细介绍)
{
int i;
struct memclust_struct * cluster;
struct memdesc_struct * memdesc;

/* initialize mem_map[] */
start_mem = free_area_init(start_mem, end_mem);/*遍历查找内存的空闲页*/

/* find free clusters, update mem_map[] accordingly */
memdesc = (struct memdesc_struct *)
(hwrpb->mddt_offset + (unsigned long) hwrpb);
cluster = memdesc->cluster;
for (i = memdesc->numclusters ; i > 0; i--, cluster++) {
unsigned long pfn, nr;

/* Bit 0 is console/PALcode reserved. Bit 1 is
non-volatile memory -- we might want to mark
this for later */
if (cluster->usage & 3)
continue;
pfn = cluster->start_pfn;
if (pfn >= MAP_NR(end_mem)) /* if we overrode mem size */
continue;
nr = cluster->numpages;
if ((pfn + nr) > MAP_NR(end_mem)) /* if override in cluster */
nr = MAP_NR(end_mem) - pfn;

while (nr--)
clear_bit(PG_reserved, &mem_map[pfn++].flags);
}

memset((void *) ZERO_PAGE(0), 0, PAGE_SIZE);

return start_mem;
}

trap_init(); 初始化硬件中断
/arch/i386/kernel/traps.c文件里定义此函数

sched_init() 初始化调度
/kernel/sched.c文件里有详细的调度算法(这些会在以后进程管理和调度的结构分析中详细介绍)

parse_options(command_line) 分析传给内核的各种选项(随后再详细介绍)

memory_start = console_init(memory_start,memory_end) 初始化控制台

memory_start = kmem_cache_init(memory_start, memory_end) 初始化内核内存cache(同样,在以后的内存管理分析中介绍此函数)

sti();接受硬件中断

kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
current->need_resched = 1; need_resched标志增加,调用schedule(调度里面会详细说明)
cpu_idle(NULL) 进入idle循环以消耗空闲的cpu时间片

已经基本完成内核初始化工作,已经把需要完成的少量责任传递给了init,所身于地工作不过是进入idle循环以消耗空闲的cpu时间片。所以在这里调用了cpu_idle(NULL),它从不返回,所以当有实际工作好处理时,该函数就会被抢占。

parse_options函数:
static void __init parse_options(char *line)/*参数收集在一条长命令行中,内核被赋给指向该命令行头部的指针*/
{
char *next;
char *quote;
int args, envs;

if (!*line)
return;
args = 0;
envs = 1;/* TERM is set to 'linux' by default */
next = line;
while ((line = next) != NULL) {

quote = strchr(line,'"');
next = strchr(line, ' ');
while (next != NULL && quote != NULL && quote < next) {

next = strchr(quote+1, '"');
if (next != NULL) {
quote = strchr(next+1, '"');
next = strchr(next+1, ' ');
}
}
if (next != NULL)
*next++ = 0;
/*
* check for kernel options first..
*/
if (!strcmp(line,"ro")) {
root_mountflags |= MS_RDONLY;
continue;
}
if (!strcmp(line,"rw")) {
root_mountflags &= ~MS_RDONLY;
continue;
}
if (!strcmp(line,"debug")) {
console_loglevel = 10;
continue;
}
if (!strcmp(line,"quiet")) {
console_loglevel = 4;
continue;
}
if (!strncmp(line,"init=",5)) {
line += 5;
execute_command = line;
args = 0;
continue;
}
if (checksetup(line))
continue;

if (strchr(line,'=')) {
if (envs >= MAX_INIT_ENVS)
break;
envp_init[++envs] = line;
} else {
if (args >= MAX_INIT_ARGS)
break;
argv_init[++args] = line;
}
}
argv_init[args+1] = NULL;
envp_init[envs+1] = NULL;
}



[这个贴子最后由e4gle在 2002/08/25 06:30pm 编辑]

////////////////////////////////////////////////////////////////////
// setup.txt
// Copyright(C) 2001, Feiyun Wang
////////////////////////////////////////////////////////////////////
// analysis on linux/arch/i386/boot/setup.S (for linux 2.2.17)
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
// export the margin tags for .text, .data and .bss
{
.text
begtext:

.data
begdata:

.bss
begbss:
}

////////////////////////////////////////////////////////////////////
.text
start()
SYSSEG = 0x1000
SETUPSEG = 0x9020
modelist = end of .text:
{
// if loaded by bootsect.S,
// you can assume CS=SETUPSEG (=0x9020), otherwise...
goto start_of_setup();

/*
http://lxr.linux.no/source/Documentation/i386/boot.txt
Offset/Size Proto Name Meaning
01F1/1 ALL setup_sects The size of the setup in sectors
01F2/2 ALL root_flags If set, the root is mounted readonly
01F4/2 ALL syssize DO NOT USE - for bootsect.S use only
01F6/2 ALL swap_dev DO NOT USE - obsolete
01F8/2 ALL ram_size DO NOT USE - for bootsect.S use only
01FA/2 ALL vid_mode Video mode control
01FC/2 ALL root_dev Default root device number
01FE/2 ALL boot_flag 0xAA55 magic number
0200/2 2.00+ jump Jump instruction
0202/4 2.00+ header Magic signature "HdrS"
0206/2 2.00+ version Boot protocol version supported
0208/4 2.00+ realmode_swtch Boot loader hook
020C/4 2.00+ start_sys_seg Points to kernel version string
0210/1 2.00+ type_of_loader Boot loader identifier
0211/1 2.00+ loadflags Boot protocol option flags
0212/2 2.00+ setup_move_size Move to high memory size (used with hooks)
0214/4 2.00+ code32_start Boot loader hook
0218/4 2.00+ ramdisk_image initrd load address (set by boot loader)
021C/4 2.00+ ramdisk_size initrd size (set by boot loader)
0220/4 2.00+ bootsect_kludge DO NOT USE - for bootsect.S use only
0224/4 2.01+ heap_end_ptr Free memory after setup end
0226/2 N/A pad1 Unused
0228/4 2.02+ cmd_line_ptr 32-bit pointer to the kernel command line
*/
.ascii "HdrS"
.word 0x0201
realmode_swtch: // boot loader hook
.word 0, 0
start_sys_seg: // pointer to kernel version string
.word SYSSEG
.word kernel_version
type_of_loader:
.byte 0
loadflags:
#ifndef __BIG_KERNEL__
.byte 0x00
#else
.byte LOADED_HIGH = 1
#endif
setup_move_size:
.word 0x8000
code32_start: // boot loader hook
#ifndef __BIG_KERNEL__
.long 0x1000
#else
.long 0x100000
#endif
ramdisk_image: // initrd load address (set by boot loader)
.long 0
ramdisk_size: // initrd size (set by boot loader)
.long 0
bootsect_kludge: // DO NOT USE - for bootsect.S use only
.word bootsect_helper(), SETUPSEG
heap_end_ptr: // free memory after setup end
.word modelist+1024
}

////////////////////////////////////////////////////////////////////
// check signature to see if all code loaded
start_of_setup()
{
// get disk type, bootlin depends on this
// http://www.ctyme.com/intr/rb-0639.htm
int13h/AH=15h(AL=0, DL=0x81);

#ifdef SAFE_RESET_DISK_CONTROLLER
int13h/AH=0(AL=0, DL=0x80); // reset hd0
#endif

// check signature at the end of setup code
if (setup_sig1!=SIG1 || setup_sig2!=SIG2) {
// since size of setup may > 4 sectors,
// the rest code may be loaded at SYSSEG
goto bad_sig;
}
goto goodsig1;
}

////////////////////////////////////////////////////////////////////
// some small functions
prtstr() { /* ... print ASCIIz string at DS:SI */}
prtsp2() { /* ... print double space */ }
prtspc() { /* ... print single space */ }
prtchr() { /* ... print ASCII AL */ }
beep() { /* ... beep */ }

////////////////////////////////////////////////////////////////////
goodsig1() { goto goodsig; } // making near jumps

////////////////////////////////////////////////////////////////////
// move rest setup code from SYSSEG:0 to CS:0800
// TODO: it won't work if image loaded at 0x100000?
bad_sig()
DELTA_INITSEG = 0x0020 (= SETUPSEG - INITSEG)
SYSSEG = 0x1000
{
BX = (CS-DELTA_INITSEG):[497]; // i.e. setup_sects
// first 4 sectors have been loaded
CX = (BX - 4) << 8; // rest code in words
start_sys_seg = (CX >> 3) + SYSSEG; // real system code start

move SYSSEG:0 to CS:0800 (CX*2 bytes);

if (setup_sig1!=SIG1 || setup_sig2!=SIG2) {
prtstr("No setup signature found ...");
halt;
}
}

////////////////////////////////////////////////////////////////////
// check if loader compatible with image
good_sig()
LOADHIGH = 1
{
if ((loadflags & LOADHIGH) && (!type_of_loader)) {
// Nope, old loader want to load big kernel
prtstr("Wrong loader: giving up.");
halt;
}
}

////////////////////////////////////////////////////////////////////
// get memory size
// set the keyboard repeat rate to max
// check video adapter
// get hd0 & hd1 data
// check for Micro Channel (MCA) bus
// check for PS/2 pointing device
// check for APM BIOS
// move code to INITSEG/SETUPSEG
// load IDT and GDT
// enable and test A20
// reset coprocessor
// reprogram the interrupts
// switch to protected mode
// goto KERNEL
loader_ok()
SETUPSET = 0x9020
INITSEG = 0x9000
DELTA_INITSEG = 0x20
{
// DS = CS - DELTA_INITSEG when entering this function

// get memory size
#ifndef STANDARD_MEMORY_BIOS_CALL
(double word)DS:[0x1E0] = 0;
try {
// get memory size for >64M configurations
// http://www.ctyme.com/intr/rb-1739.htm
int15h/AX=E801h;
// AX = extended memory between 1M and 16M, in KB
// BX = extended memory above 16M, in 64K blocks
(double word)DS:[0x1E0] = ((EBX & 0xFFFF) << 6)
+ (EAX & 0xFFFF);
}
#else
(double word)DS:[0x1E0] = 0;
#endif

// get extended memory size
// http://www.ctyme.com/intr/rb-1529.htm
int15h/AH=88h;
DS:[2] = AX; // KB of contiguous memory from 100000h

// set the keyboard repeat rate to max
// http://www.ctyme.com/intr/rb-1757.htm
int16h/AX=0305h(BX=0);

// check video adapter and its parameters, see video.S
video();

// get hd0 & hd1 data
// http://www.ctyme.com/intr/rb-6135.htm
// http://www.ctyme.com/intr/rb-6184.htm
// pointers in 0:0104 & 0:0118 respectively
move hd0 data to CS-DELTA_INITSEG:0080 (16 bytes);
move hd1 data to CS-DELTA_INITSEG:0090 (16 bytes);

// get disk type, check if hd1 exists
// http://www.ctyme.com/intr/rb-0639.htm
int13h/AH=15h(AL=0, DL=0x81);
if (failed || AH!=03h) { // AH=03h if is a hard disk
clear CS-DELTA_INITSEG:0090 (16 bytes);
}

// check for Micro Channel (MCA) bus
DS:[0xA0] = 0; // set table length to 0
try {
// get system configuration
// http://www.ctyme.com/intr/rb-1594.htm
int15h/AH=C0h; // ES:BX = ROM configuration table
move ROM configuration table to CS-DELTA_INITSEG:00A0;
// first 16 bytes only
}

// check PS/2 pointing device
DS:[0x1FF] = 0;
// get equipment list
// http://www.ctyme.com/intr/rb-0575.htm
int11h();
if (has psmouse) {
DS:[0x1FF] = 0xAA;
}

#ifdef CONFIG_APM
// check for APM BIOS

DS:[0x40] = 0;
// Advanced Power Management v1.0+ - installation check
// http://www.ctyme.com/intr/rb-1394.htm
int15h/AX=5300h(BX=0);
// check both CF and BX for APM support
if (APM && 32-bit protected mode interface supported) {
int15h/AX=5304h(BX=0); // disconnect interface
try {
// clear return values first
clear EBX, CX, DX, ESI, DI;
// connect 32bit protect mode APM interface
// http://www.ctyme.com/intr/rb-1397.htm
int15h/AX=5303h(BX=0);
if (supported) {
DS:[0x42] = 32-bit code segment base address;
DS:[0x44] = offset of entry point;
DS:[0x48] = 16-bit code segment base address;
DS:[0x4A] = 16-bit data segment base address;
DS:[0x4E] = APM BIOS code segment length;
DS:[0x52] = APM BIOS data segment length;
int15h/AX=5300h(BX=0); // check again
if (APM supported) {
INITSET:[0x40] = APM version;
INITSET:[0x4C] = APM flags;
}
else { // should not happen
// disconnect interface
int15h/AX=5304h(BX=0);
}
}
else {
// clear 32bit support
INITSET:[0x4C] &= ~0x0002;
}
}
}
#endif

// call mode switch
if (realmode_swtch) {
far realmode_swtch(); // mode switch hook
}
else {
far default_switch(); // see below
}

// set code32: 0x100000 for big kernel, otherwise 0x1000
(double word) code32 = code32_start;
if (!(loadflags & LOADED_HIGH)) {
// normal low loaded zImage
move start_sys_seg:0 to (0100:0 ... CS-DELTA_INITSEG:0);
// move 0x1000 bytes each time
}
if (CS!=SETUPSEG) {
cli; // disable interrupts

// store new SS in DX
DX = SS;
AX = CS - DELTA_INITSEG;
if (DX>=AX) {
DX = DX + INITSEG - AX; // i.e. SS-CS+SETUPSEG
}

// move CS-DELTA_INITSEG:0 to INITSEG:0 (setup_move_size bytes)
// TODO: why not do this in one step?
ES = INITSEG;
move _DOWNWARD_ from CS-DELTA_INITSEG:setup_move_size-1 to
(INITSEG:setup_move_size-1
... INITSEG:move_self_here+0x200);
// setup_move_size-move_self_here-0x200 bytes
// INITSEG:move_self_here+0x200 = SETUPSEG:move_self_here
goto SETUPSEG:move_self_here;
move_self_here:
move the rest to INITSEG:move_self_here+0x200-1 ... INITSEG:0;
// move_self_here+0x200 bytes
DS = SETUPSEG;
SS = DX;
}
// CS==SETUPSEG is true now

// Protected Mode Basics
// http://x86.ddj.com/articles/pmbasics/tspec_a1_doc.htm

lidt idt_48; // load idt with 0, 0;
// new code added here in linux 2.4
lgdt gdt_48; // load gdt with whatever appropriate;

// enable A20
empty_8042();
outportb(0x64, 0xD1); // command write
empty_8042();
outportb(0x60, 0xDF); // A20 on
empty_8042();

#define TEST_ADDR 0x7C

// test A20
GS = AX = 0xFFFF;
BX = 0:[TEST_ADDR];
do {
0:[TEST_ADDR] = ++AX;
} while (AX==GS:[TEST_ADDR+0x10]);
0:[TEST_ADDR] = BX;

// reset coprocessor
outportb(0xF0, 0);
delay();
outportb(0xF1, 0);
delay();

// reprogram the interrupts
outportb(0x20, 0x11); // initialize 8259A-1
delay();
outportb(0xA0, 0x11); // initialize 8259A-2
delay();
outportb(0x21, 0x20); // start of hardware int's (0x20)
delay();
outportb(0xA1, 0x28); // start of hardware int's 2 (0x28)
delay();
outportb(0x21, 0x04); // 8259-1 is master
delay();
outportb(0xA1, 0x02); // 8259-2 is slave
delay();
outportb(0x21, 0x01); // 8086 mode
delay();
outportb(0xA1, 0x01); // 8086 mode
delay();
outportb(0xA1, 0xFF); // mask off all interrupts for now
delay();
outportb(0x21, 0xFB); // mask all irq's but irq2, which is cascaded

// protected mode!
mov ax, #1;
lmsw ax;
jmp flush_instr;
flush_instr:
xor bx, bx;
}

////////////////////////////////////////////////////////////////////
{
db 0x66, 0xea
code32:
dd 0x1000
dw __KERNEL_CS // 0x10, defined in asm-i386/segment.h
// goto 10:1000 or 10:100000

kernel_version:
.ascii UTS_RELEASE // defined in makefile
.ascii "("
.ascii LINUX_COMPILE_BY
.ascii "@"
.ascii LINUX_COMPILE_HOST
.ascii ")"
.ascii UTS_VERSION
db 0
}

////////////////////////////////////////////////////////////////////
// default real mode switch routine
far default_switch()
{
cli;
outportb(0x70, 0x80); // disable NMI
}

////////////////////////////////////////////////////////////////////
// get called when using bootsect loader _AND_ have bzImage to load
far bootsect_helper(ES)
{
if (bootsect_es==0) {
type_of_loader = 0x20; // bootsect-loader, version 0
AX = ES >> 4;
CS:[bootsect_src_base+2] = AH;
bootsect_es = ES;
AX = ES - SYSSEG;
}
else {
if (BX==0) { // 64K aligned
// copy extended memory
// http://www.ctyme.com/intr/rb-1527.htm
try {
int15h/AX=87h(CX=0x8000,
ES:SI=CS:bootsect_gdt);
}
catch {
bootsect_panic:
prtstr("INT15 refuses to access high memory."
" Giving up.");
halt;
}
ES = bootsect_es;
CS:[bootsect_dst_base+2]++;
}
AH = CS:[bootsect_dst_base+2] << 4;
AL = 0;
}
}

////////////////////////////////////////////////////////////////////
{
bootsect_gdt:
.word 0, 0, 0, 0
.word 0, 0, 0, 0
bootsect_src:
.word 0xFFFF
bootsect_src_base:
.byte 0, 0, 1 ! base = 0x010000
.byte 0x93 ! typbyte
.word 0 ! limit16, base24 =0
bootsect_dst:
.word 0xFFFF
bootsect_dst_base:
.byte 0, 0, 0x10 ! base = 0x100000
.byte 0x93 ! typbyte
.word 0 ! limit16, base24 =0
.word 0, 0, 0, 0 ! used by BIOS
.word 0, 0, 0, 0
bootsect_es:
.word 0
}

////////////////////////////////////////////////////////////////////
// check that the keyboard command queue is empty
empty_8042()
{
timeout = 0xFFFFFF; // local variable
for (;;) {
if (--timeout==0) return;
delay();
inportb(0x64, AL); // 8042 status port
if (AL & 1) { // output buffer
delay();
inportb(0x60, .);
continue;
}
if (!(AL & 2)) return; // input buffer
}
}

////////////////////////////////////////////////////////////////////
// read the CMOS clock, return the seconds in AL
gettime()
{
// get real-time clock time
// http://www.ctyme.com/intr/rb-2273.htm
int1Ah/AH=02h(); // DH = seconds (in BCD)
AL = DH & 0x0F;
AH = DH >> 4;
AAD;
}

////////////////////////////////////////////////////////////////////
delay() { /* needed after doing I/O */ }

////////////////////////////////////////////////////////////////////
{
gdt:
.word 0,0,0,0 ! dummy
.word 0,0,0,0 ! unused

.word 0xFFFF ! 4Gb - (0x100000*0x1000 = 4Gb)
.word 0x0000 ! base address=0
.word 0x9A00 ! code read/exec
.word 0x00CF ! granularity=4096, 386 (+5th nibble of limit)

.word 0xFFFF ! 4Gb - (0x100000*0x1000 = 4Gb)
.word 0x0000 ! base address=0
.word 0x9200 ! data read/write
.word 0x00CF ! granularity=4096, 386 (+5th nibble of limit)
idt_48:
.word 0 ! idt limit=0
.word 0, 0 ! idt base=0L
gdt_48:
.word 0x800 ! gdt limit=2048, 256 GDT entries
.word 512+gdt, 0x9 ! gdt base = 0x9XXXX
// +512 because of bootsect
}

////////////////////////////////////////////////////////////////////
// Included video setup & detection code "video.S"
/*
Positions of various video parameters passed to the kernel
(see also include/linux/tty.h)
CS-DELTA_INITSEG segment
#define PARAM_CURSOR_POS 0x00
#define PARAM_VIDEO_PAGE 0x04
#define PARAM_VIDEO_MODE 0x06
#define PARAM_VIDEO_COLS 0x07
#define PARAM_VIDEO_EGA_BX 0x0a
#define PARAM_VIDEO_LINES 0x0e
#define PARAM_HAVE_VGA 0x0f
#define PARAM_FONT_POINTS 0x10
#define PARAM_LFB_WIDTH 0x12
#define PARAM_LFB_HEIGHT 0x14
#define PARAM_LFB_DEPTH 0x16
#define PARAM_LFB_BASE 0x18
#define PARAM_LFB_SIZE 0x1c
#define PARAM_LFB_LINELENGTH 0x24
#define PARAM_LFB_COLORS 0x26
#define PARAM_VESAPM_SEG 0x2e
#define PARAM_VESAPM_OFF 0x30
#define PARAM_LFB_PAGES 0x32
*/
video()
{
FS = DS;
DS = ES = CS;
GS = 0;
basic_detect();

#ifdef CONFIG_VIDEO_SELECT
if (FS:[0x1FA]!=ASK_VGA) {
// ASK_VGA=0xFFFD, defined in asm-i386/boot.h
mode_set();
if (failed) {
prtstr("You passed an undefined mode number.");
}
}
else {
mode_menu();
}

#ifdef CONFIG_VIDEO_RETAIN
restore_screen();
#endif

#endif

mode_params();
}

////////////////////////////////////////////////////////////////////
SIG1 = 0xAA55
SIG2 = 0x5A5A
{
setup_sig1: .word SIG1
setup_sig2: .word SIG2
modelist:

.text
endtext:

.data
enddata:

.bss
endbss:
}

////////////////////////////////////////////////////////////////////
// end of file







WSS(http://www.whitecell.org)
一个非营利性民间技术组织
致力于各种系统安全技术的研究
坚持传统的hacker精神
追求技术的精纯

编辑  发贴时间2002/08/25 06:23pm 此 IP 为代理服务器IP: 已设置保密
该用户目前不在线 e4gle
 帅哥 此人为版主
头衔: 论坛版主 午马 天秤座



级别: 精灵
魅力: 2286
经验: 1824
金钱: 5165 幽妮石
来自: WSS 
总发贴数: 232 篇
注册日期: 2002/08/14
消息 查看 搜索 好友 邮件 主页 复制 引用 回复贴子回复 

[这个贴子最后由e4gle在 2002/08/25 06:30pm 编辑]

////////////////////////////////////////////////////////////////////
// bootsect.txt
////////////////////////////////////////////////////////////////////
// Copyright(C) 2001, Feiyun Wang
// analysis on linux/arch/i386/boot/bootsect.S (linux 2.2.17)
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
#if 0
int 3;// for debugger
#endif

_main()
BOOTSEG = 0x07C0
INITSEG = 0x9000
{
move BOOTSEG:0 to INITSEG:0 (512 bytes);
goto INITSEG:go; // CS:IP = INITSEG:go
}

////////////////////////////////////////////////////////////////////
// prepare disk parameter table
go()
INITSEG = 0x9000
{
set SS:SP to INITSEG:3FF4;// 0x4000-0x0C
copy disk parameter table (pointer in 0:0078)
to INITSEG:3FF4 (12 bytes);
patch sector count to 36 (offset 4 in parameter table, 1 byte);
set disk parameter table pointer (0:0078) to INITSEG:3FF4;
}

////////////////////////////////////////////////////////////////////
// load the setup-sectors directly after the bootblock
load_setup()
setup_sects = SETUPSECS = 4
INITSEG = 0x9000
{
for(;;) {
int13h/AH=0(DL=0); // reset FDC
try {
// http://www.ctyme.com/intr/rb-0607.htm
int13h/AH=02h(AL=setup_sects,
ES:BX=INITSEG:0200,
CX=2, DX=0);
break;
}
catch (disk error) {
print_nl();
print_hex(SP);
}
}
}

////////////////////////////////////////////////////////////////////
// get disk drive parameters, specifically sectors#/track
ok_load_setup()
global variables: disksizes, sectors
{
#if 0
// get disk drive parameters
// http://www.ctyme.com/intr/rb-0621.htm
int13h/AH=08h(DL=0);
CH = 0;
// seems not completed yet
#else
// probe sectors with disksize[] = {36, 18, 15, 9}
SI = &disksizes;
for (;;) {
sectors = DS:[SI++];
if (SI>=disksizes+4) break;
try {
int13h/AH=02h(AL=1,
ES:BX=INITSEG:((setup_sects+1)<<9),
CX=sectors, DX=0);
break;
}
catch {
}
}
#endif
}

////////////////////////////////////////////////////////////////////
// print out "Loading"
// load the system image
// set root_dev
// jump to the setup-routine loaded directly after the bootblock
got_sectors()
INITSEG = 0x9000
SYSSEG = 0x1000
SETUPSEG = 0x9020
global variable: root_dev
{
// int10h/AH=03h http://www.ctyme.com/intr/rb-0088.htm
// int10h/AH=13h http://www.ctyme.com/intr/rb-0210.htm
print out "Loading";

read_it(ES=SYSSEG);
kill_motor();
print_nl();

if (!root_dev) {
switch (sectors) {
case 15: root_dev = 0x0208;// /dev/ps0 - 1.2Mb
break;
case 18: root_dev = 0x021C;// /dev/PS0 - 1.44Mb
break;
case 36: root_dev = 0x0220;// /dev/fd0H2880 - 2.88Mb
break;
default: root_dev = 0x0200;// /dev/fd0 - autodetect
break;
}
}

goto SETUPSEG:0;
// see linux/arch/i386/boot/setup.S
}

word sread = 0; // sectors read of current track
word head = 0; // current head
word track = 0; // current track
////////////////////////////////////////////////////////////////////
// load the system image
read_it(ES)
setup_sects = SETUPSECS = 4
SYSSEG = 0x1000
syssize = SYSSIZE = 0x7F00
{
sread = setup_sects + 1; // plus 1 bootsect
if (ES & 0x0fff) halt; // not 64KB aligned
BX = 0;

for (;;) {
rp_read:
#ifdef __BIG_KERNEL__
.word 0x1eff, 0x220;
// call far * bootsect_kludge, see setup.S
#else
AX = ES - SYSSEG;
#endif
if (AX>syssize) return;

ok1_read:
// get proper AL (sectors to read),
// not to across tracks or make BX overflow
AX = sectors - sread;
CX = BX + (AX << 9);
// TODO: I think CX!=0 can be omitted
if (CX overflow && CX!=0) {
AX = (-BX) >> 9;
}
ok2_read:
read_track(AL, ES:BX);
CX = AX;
AX += sread;
if (AX==sectors) {
if (head==1) track++;
head = 1 - head;
AX = 0;
}
ok3_read:
sread = AX;
BX += CX << 9;
if (BX overflow) {
ES += 0x1000;
BX = 0;
}
}
}

////////////////////////////////////////////////////////////////////
// read disk with (sread, track, head)
read_track(AL, ES:BX)
{
for (;;) {
printf(".");

// set CX, DX according to (sread, track, head)
DX = track;
CX = sread + 1;
CH = DL;

DX = head;
DH = DL;
DX &= 0x0100;

try {
int13h/AH=02h(AL, ES:BX, CX, DX);
return;
}
catch (disk error) {
bad_rt:
print_all();
int13h/AH=0h(DL=0); // reset FDC
}
}
}

////////////////////////////////////////////////////////////////////
// some small functions
print_all() { /* ... print out error, AX, BX, CX, DX */ }
print_nl() { /* ... print CR LF */ }
print_hex() { /* ... print the word pointed by SS:BP in hex */ }
kill_motor() { outportb(0x3F2, 0); /* turn off floppy drive motor */}

////////////////////////////////////////////////////////////////////
// global variables for bootsect.S
{
sectors:
.word 0
disksizes:
.byte 36, 18, 15, 9
msg1:
.byte 13, 10
.ascii "Loading"

/*
http://lxr.linux.no/source/Documentation/i386/boot.txt
Offset/Size Proto Name Meaning
01F1/1 ALL setup_sects The size of the setup in sectors
01F2/2 ALL root_flags If set, the root is mounted readonly
01F4/2 ALL syssize DO NOT USE - for bootsect.S use only
01F6/2 ALL swap_dev DO NOT USE - obsolete
01F8/2 ALL ram_size DO NOT USE - for bootsect.S use only
01FA/2 ALL vid_mode Video mode control
01FC/2 ALL root_dev Default root device number
01FE/2 ALL boot_flag 0xAA55 magic number
*/
.org 497
setup_sects:
.byte SETUPSECS
root_flags:
.word CONFIG_ROOT_RDONLY
syssize:
.word SYSSIZE
swap_dev:
.word SWAP_DEV
ram_size:
.word RAMDISK
vid_mode:
.word SVGA_MODE
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55
}

////////////////////////////////////////////////////////////////////
// end of file




[这个贴子最后由e4gle在 2002/08/25 06:30pm 编辑]

////////////////////////////////////////////////////////////////////
// head.txt
// Copyright(C) 2001, Feiyun Wang
////////////////////////////////////////////////////////////////////
// analysis on linux/arch/i386/kernel/head.S (for linux 2.2.17)
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
startup_32()
__KERNEL_DS = 0x18;
CL_MAGIC = 0xA33F;
CL_MAGIC_ADDR = 0x90020;
CL_BASE_ADDR = 0x90000;
CL_OFFSET = 0x90022;
{
cld;
DS = ES = FS = GS = __KERNEL_DS (= 0x18);

#ifdef __SMP__
if (BX) {
CR4 |= mmu_cr4_features-__PAGE_OFFSET;
// mmu_cr4_features defined in arch/i386/mm/init.c
// __PAGE_OFFSET defined in include/asm-i386/page.h
}
#endif

CR3 = 0x101000;// page table pointer
CR0 |= 0x80000000;// set PG bit

SS:ESP = stack_start;

#ifdef __SMP__
if (BX) {
EFLAG = 0;
goto checkCPUtype;
}
#endif

clear BSS (__bss_start .. _end);
setup_idt();
EFLAGS = 0;
move 0x90000 to empty_zero_page (i.e. 0x5000) (2 KByte);
clear empty_zero_page+2K (2 KByte);

if (CL_MAGIC==*(CL_MAGIC_ADDR)) {
move *(CL_OFFSET)+CL_BASE_ADDR to empty_zero_page+2K (2 KByte);
}
}

////////////////////////////////////////////////////////////////////
checkCPUtype()
global variables: ready
// see include/asm-i386/processor.h struct cpuinfo_x86
// boot_cpu_data defined in arch/i386/kernel/setup.c
struct cpuinfo_x86 boot_cpu_data;
__KERNEL_DS = 0x18;
{
X86_CPUID = -1;

X86 = 3;
save original EFLAGS;
check AC bit in EFLAGS;
if (AC bit not changed) goto is386;

X86 = 4;
check ID bit in EFLAGS;
restore original EFLAGS;
if (ID bit not changed) goto is486;

// get CPU info,
// <> Vol.2 P.3-110
CPUID(EAX=0);
X86_CPUID = EAX;
X86_VENDOR_ID = EBX;
*((&X86_VENDOR_ID)+4) = ECX;
*((&X86_VENDOR_ID)+8) = EDX;
if (!EAX) goto is486;

CPUID(EAX=1);
CL = AL;
X86 = AH & 0x0f;// family
X86_MODEL = (AL & 0xf0) >> 4;// model
X86_MASK = AL & 0x0f;// stepping id
X86_CAPABILITY = EDX;// feature

is486:
// save PG, PE & ET, set AM, WP, NE & MP
EAX = (CR0 & 0x80000011) | 0x50022;
goto 2f;
is386:
restore original EFLAGS;
// save PG, PE & ET, set MP
EAX = (CR0 & 0x80000011) | 0x02;
2f:
CR0 = EAX;
check_x87();

#ifdef __SMP__
if (ready) {
CR4 |= 0x10;// set PSE, turn on 4 MByte pages
CR3 = CR3;
}
ready++;
#endif;

lgdt gdt_descr;
lidt idt_descr;
DS = ES = FS = GS = __KERNEL_DS (= 0x18);

#ifdef __SMP__
SS = __KERNEL_DS (= 0x18);
#else
SS:ESP = stack_start;
#endif

lldt 0;
cld;
start_kernel();
halt;
}

////////////////////////////////////////////////////////////////////
{
#ifdef __SMP__
ready:.byte 0;
#endif
}

////////////////////////////////////////////////////////////////////
check_x87()
{
X86_HARD_MATH = 0;
clts;
fninit;
fstsw ax;
if (al) {
// no coprocessor, set EM;
// TODO; why not use |=?
cr0 ^= 0x04;
}
else {
X86_HARD_MATH = 1;
fsetpm;// 0xDB, 0xE4
}
}

////////////////////////////////////////////////////////////////////
setup_idt()
{
edx = &ignore_int;
eax = __KERNEL_CS << 16;
ax = dx;
dx = 0x8E00;// interrupt gate, dpl = 0, present

set all entries in idt_table to eax:edx;// 256*8 Bytes
}

////////////////////////////////////////////////////////////////////
{
stack_start:
.long init_task_union+8192;
.long __KERNEL_DS;
}

////////////////////////////////////////////////////////////////////
ignore_init()
{
printk("Unknown interrupt ");
}

////////////////////////////////////////////////////////////////////
{
NR_TASKS = 512;// defined in include/linux/tasks.h
IDT_ENTRIES = 256;
GDT_ENTRIES = 12+2*NR_TASKS;

.word 0;
idt_descr:
.word IDT_ENTRIES*8-1;
idt:
.long idt_table;

.word 0;
gdt_descr:
.word GDT_ENTRIES*8-1;
gdt:
.long gdt_table;
}

////////////////////////////////////////////////////////////////////
{
.org 0x1000;
swapper_pg_dir:
.long 0x00102007;
.fill __USER_PGD_PTRS-1, 4, 0;// 767 entries
.long 0x00102007;
.fill __KERNEL_PGD_PTRS-1, 4, 0;// 255 entries

.org 0x2000;
pg0:
// ...

.org 0x3000;
empty_bad_page:

.org 0x4000;
empty_bad_page_table:

.org 0x5000;
empty_zero_page:

.org 0x6000;
.data
gdt_table:
.quad 0x0000000000000000;// null
.quad 0x0000000000000000;// not used
.quad 0x00cf9a000000ffff;// 0x10 kernel 4GB code at 0x00000000
.quad 0x00cf92000000ffff;// 0x18 kernel 4GB data at 0x00000000
.quad 0x00cffa000000ffff;// 0x20 user 4GB code at 0x00000000
.quad 0x00cff2000000ffff;// 0x28 user 4GB data at 0x00000000
.quad 0x0000000000000000;// not used
.quad 0x0000000000000000;// not used

.quad 0x0040920000000000;// 0x40 APM setup for bad BIOS
.quad 0x00409a0000000000;// 0x48 APM CS code
.quad 0x00009a0000000000;// 0x50 APM CS 16 code (16 bit)
.quad 0x0040920000000000;// 0x58 APM DS data
.fill 2*NR_TASKS, 8, 0;

.section .text.lock
stext_lock:
}

////////////////////////////////////////////////////////////////////
// end of file