这段代码在arch/x86/boot/kernel/header_32.S中,它是在内核被解压缩到0x100000处之后跳转到这个地址开始执行的,前面的操作算是为这一步做铺垫吧,现在到了真正的内核,为了能够让自己把内核代码真的弄懂,不再采用前面那种比较比较笼统的方式了,而是按照内核源码分析的那种方式将这个代码复制下来,然后逐行的分析,这样就不至于再给自己偷懒的机会了,嗯,以后就这样做,争取能够把不懂的内容搞懂。
在这个时候,我们已经进入了保护模式(也就是寻址方式发生了改变),但是没有启用分页,所以得到的线性地址就是物理地址,之前的bootloader的内容已经对我们没有任何意义了,唯一剩余的就是它留下来一个关于一些参数结构体boot_params,这个结构体的地址保存在寄存器esi中,现在重新开始内核的执行。
代码如下:
.text
/* 省略头文件 */
/* Physical address */
#define pa(X) ((X) - __PAGE_OFFSET) //逻辑地址转换成物理地址,因为这时候已经开启了保护模式,所以内核对应着高端的1G内存,并且和物理内存直接映射
/*
* References to members of the new_cpu_data structure.
*/
#define X86 new_cpu_data+CPUINFO_x86
#define X86_VENDOR new_cpu_data+CPUINFO_x86_vendor
#define X86_MODEL new_cpu_data+CPUINFO_x86_model
#define X86_MASK new_cpu_data+CPUINFO_x86_mask
#define X86_HARD_MATH new_cpu_data+CPUINFO_hard_math
#define X86_CPUID new_cpu_data+CPUINFO_cpuid_level
#define X86_CAPABILITY new_cpu_data+CPUINFO_x86_capability
#define X86_VENDOR_ID new_cpu_data+CPUINFO_x86_vendor_id
/*
* This is how much memory in addition to the memory covered up to
* and including _end we need mapped initially.
* We need:
* (KERNEL_IMAGE_SIZE/4096) / 1024 pages (worst case, non PAE)
* (KERNEL_IMAGE_SIZE/4096) / 512 + 4 pages (worst case for PAE)
*
* Modulo rounding, each megabyte assigned here requires a kilobyte of
* memory, which is currently unreclaimed.
*
* This should be a multiple of a page.
*
* KERNEL_IMAGE_SIZE should be greater than pa(_end)
* and small than max_low_pfn, otherwise will waste some page table entries
*/
#if PTRS_PER_PMD > 1
#define PAGE_TABLE_SIZE(pages) (((pages) / PTRS_PER_PMD) + PTRS_PER_PGD)
#else
#define PAGE_TABLE_SIZE(pages) ((pages) / PTRS_PER_PGD) //pages页需要的页表项
#endif
/* Number of possible pages in the lowmem region */
LOWMEM_PAGES = (((1<<32) - __PAGE_OFFSET) >> PAGE_SHIFT) //内核能够映射的物理内存的大小,低端内存
/* Enough space to fit pagetables for the low memory linear map */
MAPPING_BEYOND_END = PAGE_TABLE_SIZE(LOWMEM_PAGES) << PAGE_SHIFT //低端内存所占用的页表项的大小
/*
* Worst-case size of the kernel mapping we need to make:
* a relocatable kernel can live anywhere in lowmem, so we need to be able
* to map all of lowmem.
*/
KERNEL_PAGES = LOWMEM_PAGES //内核能够映射的内存大小
INIT_MAP_SIZE = PAGE_TABLE_SIZE(KERNEL_PAGES) * PAGE_SIZE //同MAPPING_BEYOND_END
RESERVE_BRK(pagetables, INIT_MAP_SIZE)
/*
* 32-bit kernel entrypoint; only used by the boot CPU. On entry,
* %esi points to the real-mode code as a 32-bit pointer.
* CS and DS must be 4 GB flat segments, but we don't depend on
* any particular GDT layout, because we load our own as soon as we
* can.
*/
__HEAD
ENTRY(startup_32) //0x100000,从这里开始执行内核的代码
movl pa(stack_start),%ecx //这里设置堆栈
/* test KEEP_SEGMENTS flag to see if the bootloader is asking
us to not reload segments */
testb $(1<<6), BP_loadflags(%esi) //判断是否需要重新设置各个段寄存器
jnz 2f
/*
* Set segments to known values.
*/
lgdt pa(boot_gdt_descr) //设置临时的全局页描述符表,后面还需要进一步初始化
movl $(__BOOT_DS),%eax //初始化各个段寄存器
movl %eax,%ds
movl %eax,%es
movl %eax,%fs
movl %eax,%gs
movl %eax,%ss
2:
leal -__PAGE_OFFSET(%ecx),%esp //设置堆栈的栈顶指针,这个堆栈就是init_task的堆栈,这里相当于手工初始化了一个进程
/*
* Clear BSS first so that there are no surprises...
*/ //初始化bss段,清0
cld
xorl %eax,%eax
movl $pa(__bss_start),%edi //bss段的起始位置
movl $pa(__bss_stop),%ecx
subl %edi,%ecx //计算bss段的大小
shrl $2,%ecx //以4字节为单位对bss段进行清0
rep ; stosl
/*
* Copy bootup parameters out of the way.
* Note: %esi still has the pointer to the real-mode data.
* With the kexec as boot loader, parameter segment might be loaded beyond
* kernel image and might not even be addressable by early boot page tables.
* (kexec on panic case). Hence copy out the parameters before initializing
* page tables.
*/
movl $pa(boot_params),%edi //拷贝boot_params的内容
movl $(PARAM_SIZE/4),%ecx
cld
rep
movsl
movl pa(boot_params) + NEW_CL_POINTER,%esi //这里保存的是boot_params.hdr.cmd_line_ptr
andl %esi,%esi
jz 1f # No command line
movl $pa(boot_command_line),%edi
movl