/*
* swapper_pg_dir is the main page directory, address 0x00101000
* On entry, %esi points to the real-mode code as a 32-bit pointer=0x90000.
*/
ENTRY(stext)
ENTRY(_stext)
startup_32:
/*
* Set segments to known values
*/
cld
movl $(__KERNEL_DS),%eax
movl %eax,%ds
movl %eax,%es
movl %eax,%fs
movl %eax,%gs
#ifdef CONFIG_SMP
......
#endif
/***************************************************************************
#define __PAGE_OFFSET (0xC0000000)
jmp 1f /* flush the prefetch-queue */
1:
movl $1f,%eax
jmp *%eax /* make sure eip is relocated */
1:
/******************************************************************************
Set up the stack pointer:
这里lss SYMBOL_NAME(stack_start),%esp将SS设置成DS,而SP-->stack_start,从而形成
了8K的系统堆栈,栈顶为init_task_union-->stack的最后一个元素的地址:
union task_union {
struct task_struct task;
unsigned long stack[INIT_TASK_SIZE/sizeof(long)];
};
/******************************************************************************
Clear BSS first so that there are no surprises...
No need to cld as DF is already clear from cld above...
把bss对应的空间都通通清0.__bss_start _end都在vmlinux.lds里有,它们的值都比较大(对应的虚
拟地址和物理地址也就比较高)。
******************************************************************************/
xorl %eax,%eax
movl $ SYMBOL_NAME(__bss_start),%edi
movl $ SYMBOL_NAME(_end),%ecx
subl %edi,%ecx
rep /* rep stosb: Fill (E)CX bytes at ES:[(E)DI] with AL.*/
stosb
/******************************************************************************
开始32位系统的建立。我们需要重复做一些16位模式下建立16位系统的类似事情。
******************************************************************************/
call setup_idt /* 建立中断向量表 */
/******************************************************************************
在切换到保护模式之前,最好初始化所有该初始化的。
这里将psw中所有值清为0,用于初始化eflags,注意在compressed/head.S中也有这样的初始化代码,这
里为什么要再次初始化一下呢?是因为在这过程中,eflags由于各种计算的原因,很多标识已经不为0了!!!
******************************************************************************/
pushl $0
popfl
/******************************************************************************
Note: %esi still has the pointer to the real-mode data.0x90000
Copy bootup parameters out of the way. First 2kB of_empty_zero_page is for
boot parameters, second 2kB is for the command line.
struct cpuinfo_x86 {
__u8 x86; /* CPU family */
__u8 x86_vendor; /* CPU vendor */
__u8 x86_model;
__u8 x86_mask;
char wp_works_ok; /* It doesn't on 386's */
char hlt_works_ok; /* Problems on some 486Dx4's and old 386's */
char hard_math;
char rfu;
int cpuid_level; /* Maximum supported CPUID level, -1=no CPUID */
__u32 x86_capability[NCAPINTS];
char x86_vendor_id[16];
char x86_model_id[64];
int x86_cache_size; /* in KB - valid for CPUS which support this call */
int fdiv_bug;
int f00f_bug;
int coma_bug;
unsigned long loops_per_jiffy;
unsigned long *pgd_quick;
unsigned long *pmd_quick;
unsigned long *pte_quick;
unsigned long pgtable_cache_sz;
};
#define CPU_PARAMS SYMBOL_NAME(boot_cpu_data)
#define X86 CPU_PARAMS+0
#define X86_VENDOR CPU_PARAMS+1
#define X86_MODEL CPU_PARAMS+2
#define X86_MASK CPU_PARAMS+3
#define X86_HARD_MATH CPU_PARAMS+6
#define X86_CPUID CPU_PARAMS+8
#define X86_CAPABILITY CPU_PARAMS+12
#define X86_VENDOR_ID CPU_PARAMS+16
*/
******************************************************************************/
movl $-1,X86_CPUID /* -1 for no CPUID initially */
/*
* check if it is 486 or 386.
* XXX - this does a lot of unnecessary setup. Alignment checks don't
* apply at our cpl of 0 and the stack ought to be aligned already, and
* we don't need to preserve eflags.
*/
/* 判断是否是386,先设置boot_cpu_data.x86=3(386) */
movl $3,X86 # at least 386
pushfl # push EFLAGS
popl %eax # get EFLAGS
movl %eax,%ecx # save original EFLAGS
xorl $0x40000,%eax # flip AC bit in EFLAGS
pushl %eax # copy to EFLAGS
popfl # set EFLAGS
pushfl # get new EFLAGS
popl %eax # put it in eax
xorl %ecx,%eax # change in flags
andl $0x40000,%eax # check if AC bit changed
je is386 # 是386则跳转
/* 判断是否是486,设置boot_cpu_data.x86=4(486) */
movl $4,X86 # at least 486
movl %ecx,%eax
xorl $0x200000,%eax # check ID flag
pushl %eax
popfl # if we are on a straight 486DX, SX, or
pushfl # 487SX we can't change it
popl %eax
xorl %ecx,%eax
pushl %ecx # restore original EFLAGS
popfl
andl $0x200000,%eax
je is486 # 是486则跳转
/* get vendor info,如果不是486,则获取供应商ID并保存到boot_cpu_data中 */
xorl %eax,%eax # call CPUID with 0 -> return vendor ID
cpuid
movl %eax,X86_CPUID # save CPUID level
movl %ebx,X86_VENDOR_ID # lo 4 chars
movl %edx,X86_VENDOR_ID+4 # next 4 chars
movl %ecx,X86_VENDOR_ID+8 # last 4 chars
orl %eax,%eax # do we have processor info as well?
je is486
/* 获取CPU类型 */
movl $1,%eax # Use the CPUID instruction to get CPU type
cpuid
movb %al,%cl # save reg for future use
andb $0x0f,%ah # mask processor family
movb %ah,X86
andb $0xf0,%al # mask model
shrb $4,%al
movb %al,X86_MODEL
andb $0x0f,%cl # mask mask revision
movb %cl,X86_MASK
movl %edx,X86_CAPABILITY
is486:
movl %cr0,%eax # 486 or better
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x50022,%eax # set AM, WP, NE and MP
jmp 2f
#Clears the DF flag in the EFLAGS register.
cld # gcc2 wants the direction flag cleared at all times
#ifdef CONFIG_SMP
.......
#endif
# 进入start_kernel内核
call SYMBOL_NAME(start_kernel)
sets up a idt with 256 entries pointing to ignore_int, interrupt gates. It
doesn't actually load idt - that can be done only after paging has been enabled
and the kernel moved to PAGE_OFFSET. Interrupts are enabled elsewhere, when we
can be relatively sure everything is ok.
/**********************************************************************
The interrupt descriptor table has room for 256 idt's,the global
descriptor table is dependent on the number of tasks we can have..
***********************************************************************/
#define IDT_ENTRIES 256
#define GDT_ENTRIES (__TSS(NR_CPUS)) #=16
/***********************************************************************
The layout of the GDT under Linux:
0 - null
1 - not used
2 - kernel code segment
3 - kernel data segment
4 - user code segment <-- new cacheline
5 - user data segment
6 - not used
7 - not used
8 - APM BIOS support <-- new cacheline
9 - APM BIOS support
10 - APM BIOS support
11 - APM BIOS support
The TSS+LDT descriptors are spread out a bit so that every CPU
has an exclusive cacheline for the per-CPU TSS and LDT:
12 - CPU#0 TSS <-- new cacheline
13 - CPU#0 LDT
14 - not used
15 - not used
**********************************************************************/
gdt_descr:
.word GDT_ENTRIES*8-1
SYMBOL_NAME(gdt):
.long SYMBOL_NAME(gdt_table)
/***********************************************************************
This is initialized to create an identity-mapping at 0-8M (for bootup
purposes) and another mapping of the 0-8M area at virtual address
PAGE_OFFSET.
/***********************************************************************
The page tables are initialized to only 8MB here - the final page
tables are set up later depending on memory size.
/***********************************************************************
empty_zero_page must immediately follow the page tables ! (The
initialization loop counts until empty_zero_page)
**********************************************************************/
.org 0x4000
ENTRY(empty_zero_page)
.org 0x5000
ENTRY(empty_bad_page)
.org 0x6000
ENTRY(empty_bad_pte_table)
#if CONFIG_X86_PAE
.org 0x7000
ENTRY(empty_bad_pmd_table)
.org 0x8000
#else
.org 0x7000
#endif
/***********************************************************************
This starts the data section. Note that the above is all in the
text section because it has alignment requirements that we cannot
fulfill any other way.
**********************************************************************/
.data
ALIGN
/***********************************************************************
This contains typically 140 quadwords, depending on NR_CPUS.
NOTE! Make sure the gdt descriptor in head.S matches this if you
change anything.
**********************************************************************/
ENTRY(gdt_table)
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0x0000000000000000 /* not used */
.quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */
.quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */
.quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */
.quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */
.quad 0x0000000000000000 /* not used */
.quad 0x0000000000000000 /* not used */
/*
* The APM segments have byte granularity and their bases
* and limits are set at run time.
*/
.quad 0x0040920000000000 /* 0x40 APM set up for bad BIOS's */
.quad 0x00409a0000000000 /* 0x48 APM CS code */
.quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */
.quad 0x0040920000000000 /* 0x58 APM DS data */
.fill NR_CPUS*4,8,0 /* space for TSS's and LDT's */
/*
* This is to aid debugging, the various locking macros will be putting
* code fragments here. When an oops occurs we'd rather know that it's
* inside the .text.lock section rather than as some offset from whatever
* function happens to be last in the .text segment.
*/
.section .text.lock
ENTRY(stext_lock)