/*
* linux/boot/head.S
*
* Copyright (C) 1991, 1992, 1993 Linus Torvalds
*/
/*
* head.S contains the 32-bit startup code.
*
* NOTE!!! Startup happens at absolute address 0x00001000, which is also where
* the page directory will exist. The startup code will be overwritten by
* the page directory. [According to comments etc elsewhere on a compressed
* kernel it will end up at 0x1000 + 1Mb I hope so as I assume this. - AC]
*
* Page 0 is deliberately kept safe, since System Management Mode code in
* laptops may need to access the BIOS data stored there. This is also
* useful for future device drivers that either access the BIOS via VM86
* mode.
*/
/*
* High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
* 这段代码负责解压内核,首先将内核移动到一个临时位置,然后再解压到加载
* 地址,一般是1M。
*/
.text
#include <linux/init.h>
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/page_types.h>
#include <asm/boot.h>
#include <asm/asm-offsets.h>
__HEAD
ENTRY(startup_32)
//EFI 启动相关
#ifdef CONFIG_EFI_STUB
jmp preferred_addr
.balign 0x10
/*
* We don't need the return address, so set up the stack so
* efi_main() can find its arugments.
*/
add $0x4, %esp
call efi_main
cmpl $0, %eax
movl %eax, %esi
jne 2f
1:
/* EFI init failed, so hang. */
hlt
jmp 1b
2:
call 3f
3:
popl %eax
subl $3b, %eax
subl BP_pref_address(%esi), %eax
add BP_code32_start(%esi), %eax
leal preferred_addr(%eax), %eax
jmp *%eax
preferred_addr:
#endif
cld
/*
* Test KEEP_SEGMENTS flag to see if the bootloader is asking
* us to not reload segments
* 在/Documentation/x86/boot.txt指出,BIT6是KEEP_SEGMENTS(段保持)
* 在32位入口处需要判断此标志。
* 0:重新设置各段寄存器为__BOOT_DS
* 1:保持原来值
*/
testb $(1<<6), BP_loadflags(%esi)
jnz 1f
cli
movl $__BOOT_DS, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %fs
movl %eax, %gs
movl %eax, %ss
1:
/*
* Calculate the delta between where we were compiled to run
* at and where we were actually loaded at. This can only be done
* with a short local call on x86. Nothing else will tell us what
* address we are running at. The reserved chunk of the real-mode
* data at 0x1e4 (defined as a scratch field) are used as the stack
* for this calculation. Only 4 bytes are needed.
* call 的压栈动作会将1f的实际地址放入esp(即scratch字段)中。
* call句会将下一条指令开始地址存入栈中,即
* 相当于push 1f,jmp offset.而对于push栈而言,执行过程类似esp-=4,
* mov 1f esp.就是说esp会先减4,然后再将值存入。这就是+4产生的原因。
* 1f对本地call指令而言,被汇编成一个偏移量,即1f离本call语句的偏移,所以
* call在运行时可准确地将其下一条指令的实际地址放入栈中。
* 而subl句它引用的1:会被汇编成一个绝对地址(理论值)。
* 所以popl后,ebp中存放的是标号1:的实际值,它减去理论值的差值再存入ebp
* 中。
*/
leal (BP_scratch+4)(%esi), %esp
call 1f
1: popl %ebp
subl $1b, %ebp
/*
* %ebp contains the address we are loaded at by the boot loader and %ebx
* contains the address where we should move the kernel image temporarily
* for safe in-place decompression.
* 既然上面ebp算出的是个delta,那么再加个offset有什么用?
* 假设编译时内核映像是以0为基址的:那在popl句中,ebp是标号1的实际地址,而标
* 号1:是它距内核映像基址的偏移量。标号1实际地址-它距内核映像基址偏移量=
* 内核映像基址的实际地址=startup_32标号的实际地址。
*
* 在vmlinux.lds.S中是startup_32入口地址,并且假设其被加载到address 0,与上
* 面假设符合。
*
* 下列代码是设置对齐
*/
#ifdef CONFIG_RELOCATABLE
movl %ebp, %ebx
movl BP_kernel_alignment(%esi), %eax
decl %eax
addl %eax, %ebx
notl %eax
andl %eax, %ebx
#else
movl $LOAD_PHYSICAL_ADDR, %ebx
#endif
/*
* Target address to relocate to for decompression
* 在对齐后的地址上加一个偏移作为解压地址
*/
addl $z_extract_offset, %ebx
/* Set up the stack */
leal boot_stack_end(%ebx), %esp
/* Zero EFLAGS */
pushl $0
popfl
/*
* Copy the compressed kernel to the end of our buffer
* where decompression in place becomes safe.
* ebp是startup_32加载地址,而ebx是临时放压缩代码的地方
*/
pushl %esi
leal (_bss-4)(%ebp), %esi
leal (_bss-4)(%ebx), %edi
movl $(_bss - startup_32), %ecx
shrl $2, %ecx
std //设置标志位使esi,edi在循环中递减
rep movsl//倒着复制,因std作用
cld //清方向标志位
popl %esi
/*
* 上面代码是从startup_32开始到_bss都复制到ebx开始的地
* 方了,那下面这两句就是跳到新的、以ebx为首址的relocated
* 的位置。
* Jump to the relocated address.
*/
leal relocated(%ebx), %eax
jmp *%eax
ENDPROC(startup_32)
.text
relocated:
/*
* Clear BSS (stack is currently empty)
* bss段清零
*/
xorl %eax, %eax
leal _bss(%ebx), %edi
leal _ebss(%ebx), %ecx
subl %edi, %ecx
shrl $2, %ecx
rep stosl
/*
* Adjust our own GOT
* global offset table全局变量表
*/
leal _got(%ebx), %edx
leal _egot(%ebx), %ecx
1:
cmpl %ecx, %edx
jae 2f
addl %ebx, (%edx)
addl $4, %edx
jmp 1b
2:
/*
* Do the decompression, and jump to the new kernel..
* 在上面ebx是ebp对齐后再加上z_extract_offset的值
* 现在给ebx减去z_extract_offset就是ebp对齐的值
* 即原加载地址(startup_32)对齐后的地址(放入ebp)。
* decompress_kernel函数在/arch/x86/boot/compressed/misc.c
* 中定义
*/
leal z_extract_offset_negative(%ebx), %ebp
/* push arguments for decompress_kernel: */
pushl %ebp /* output address */
pushl $z_input_len /* input_len 压缩内核大小*/
leal input_data(%ebx), %eax
pushl %eax /* input_data压缩内核开始地址 */
leal boot_heap(%ebx), %eax
pushl %eax /* heap area 工作堆*/
pushl %esi /* real mode pointer 是boot_params*/
call decompress_kernel
addl $20, %esp //恢复栈,5个参数
#if CONFIG_RELOCATABLE
/*
* Find the address of the relocations.
*/
leal z_output_len(%ebp), %edi
/*
* Calculate the delta between where vmlinux was compiled to run
* and where it was actually loaded.
*/
movl %ebp, %ebx
subl $LOAD_PHYSICAL_ADDR, %ebx
jz 2f /* Nothing to be done if loaded at compiled addr. */
/*
* Process relocations.
*/
1: subl $4, %edi
movl (%edi), %ecx
testl %ecx, %ecx
jz 2f
addl %ebx, -__PAGE_OFFSET(%ebx, %ecx)
jmp 1b
2:
#endif
/*
* Jump to the decompressed kernel.
* ebx清零,跳到加载地址,现在加载地址上是解压后的内核
* 跳转地址还是ebp,是压缩内核被加载地址对齐后的地址
*/
xorl %ebx, %ebx
jmp *%ebp
/*
* Stack and heap for uncompression
*/
.bss
.balign 4
boot_heap:
.fill BOOT_HEAP_SIZE, 1, 0
boot_stack:
.fill BOOT_STACK_SIZE, 1, 0
boot_stack_end:
/*
* esi=boot_params(未变)
* ebp=内核加载地址(1M?)
* ebx=0
* 段寄存器=24=$__BOOT_DS
* 其它通用寄存器都有变化
* 本文用到的一些标号可以在arch/x86/boot/compressed/mkpiggy.c中找到,
* 它是编译过程中的辅助程序。 这个文件会产生piggy.S,它的主要目的就
* 是导出一些符号,由mkpiggy.c中的printf实现。
*/
linux-boot-arch_x86_boot_compressed_head_32
最新推荐文章于 2022-10-07 11:19:30 发布