📄 start32.s
字号:
/* #defines because ljmp wants a number, probably gas bug *//* .equ KERN_CODE_SEG,_pmcs-_gdt */#define KERN_CODE_SEG 0x08 .equ KERN_DATA_SEG,_pmds-_gdt/* .equ REAL_CODE_SEG,_rmcs-_gdt */#define REAL_CODE_SEG 0x18 .equ REAL_DATA_SEG,_rmds-_gdt .equ FLAT_CODE_SEG,_pmcs2-_gdt .equ FLAT_DATA_SEG,_pmds2-_gdt .equ CR0_PE,1#ifdef CONFIG_X86_64 .equ LM_CODE_SEG, _lmcs-_gdt .equ LM_DATA_SEG, _lmds-_gdt#endif .equ MSR_K6_EFER, 0xC0000080 .equ EFER_LME, 0x00000100 .equ X86_CR4_PAE, 0x00000020 .equ CR0_PG, 0x80000000#undef CODE16#if defined(PCBIOS) || defined(TAGGED_IMAGE)#define CODE16#endif #ifdef GAS291#define DATA32 data32;#define ADDR32 addr32;#define LJMPI(x) ljmp x#else#define DATA32 data32#define ADDR32 addr32/* newer GAS295 require #define LJMPI(x) ljmp *x */#define LJMPI(x) ljmp x#endif/* * NOTE: if you write a subroutine that is called from C code (gcc/egcs), * then you only have to take care of %ebx, %esi, %edi and %ebp. These * registers must not be altered under any circumstance. All other registers * may be clobbered without any negative side effects. If you don't follow * this rule then you'll run into strange effects that only occur on some * gcc versions (because the register allocator may use different registers). * * All the data32 prefixes for the ljmp instructions are necessary, because * the assembler emits code with a relocation address of 0. This means that * all destinations are initially negative, which the assembler doesn't grok, * because for some reason negative numbers don't fit into 16 bits. The addr32 * prefixes are there for the same reasons, because otherwise the memory * references are only 16 bit wide. Theoretically they are all superfluous. * One last note about prefixes: the data32 prefixes on all call _real_to_prot * instructions could be removed if the _real_to_prot function is changed to * deal correctly with 16 bit return addresses. I tried it, but failed. *//**************************************************************************START - Where all the fun begins....**************************************************************************//* this must be the first thing in the file because we enter from the top */ .text .arch i386 .global _start_start: .code32 cli /* Save the initial ebp value */ pushl %ebp /* * See where I am running, and compute virt_offset */ call 1f1: popl %ebp subl $1b, %ebp movl %ebp, virt_offset(%ebp) /* * Save the initial register contents */ movl %edi, 0 + initial_regs(%ebp) movl %esi, 4 + initial_regs(%ebp) popl %edi /* original %ebp */ movl %edi, 8 + initial_regs(%ebp) movl %esp, 12 + initial_regs(%ebp) movl %ebx, 16 + initial_regs(%ebp) movl %edx, 20 + initial_regs(%ebp) movl %ecx, 24 + initial_regs(%ebp) movl %eax, 28 + initial_regs(%ebp) /* * Setup the stack */ leal _estack(%ebp), %esp /* Fixup the gdt */ leal _pmcs(%ebp), %eax pushl %eax pushl %ebp call set_seg_base addl $8, %esp /* Fixup gdtarg */ leal _gdt(%ebp), %eax movl %eax, gdtarg +2(%ebp) /* Load the global descriptor table */ cs lgdt gdtarg(%ebp) /* reload cs */ ljmp $KERN_CODE_SEG, $1f1: /* reload other segment registers */ movl $KERN_DATA_SEG, %eax movl %eax,%ds movl %eax,%es movl %eax,%ss movl %eax,%fs movl %eax,%gs /* Fixup the stack pointer */ subl %ebp, %esp call main /* fall through */ /* exit */ /* Switch back to the original stack */ movl 12 + initial_regs, %esp subl virt_offset, %esp#ifdef CODE16 /* Make certain we don't have a null return address. */ cmpl $0, 0(%esp) je exit16#endif /* Revert to flat physical addresses and return */ call _virt_to_phys xorl %eax, %eax ret#ifdef CODE16exit16: /* We have a null return address so assume a 16bit far return * address is burried deeper on the stack. */ movzwl 4(%esp), %ebx movzwl 6(%esp), %ecx pushl $ 10f pushl $ 20f - 10f call _real_call .section ".text16"10: .code16 cli /* Switch back to the callers stack, and return */ movw %bx, %sp movw %cx, %ss popf lret20: .code32 .previous#endif /* CODE16 */ .code32#if defined(TAGGED_IMAGE)/**************************************************************************XSTART16 - Transfer control to the kernel just loaded**************************************************************************/ .globl xstart16xstart16: pushl %ebp movl %esp,%ebp pushl %ebx pushl %esi pushl %edi movl 8(%ebp),%edx movl 12(%ebp),%ebx /* FIXME handle the bootp record */ movl 16(%ebp),%ecx /* bootp record (32bit pointer) */ shll $12,%ecx /* convert to segment:offset form */ shrw $12,%cx pushl $ 10f pushl $ 20f - 10f call _real_call .section ".text16"10: .code16 popw %ax /* get the return ip addr */ pushl %ecx /* bootp record */ pushl %ebx /* file header */ pushw %cs /* Setup the far return address */ pushw %ax pushl %edx /* Setup the far address to call */ lret /* Back into the routine I'm calling */20: .code32 .previous popl %edi popl %esi popl %ebx popl %ebp ret#endif /* TAGGED_IMAGE */#if defined(RELOCATE)/**************************************************************************RELOCATE_TO - relocate etherboot to the specified address**************************************************************************/ .globl relocate_torelocate_to: /* Save the callee save registers */ pushl %ebp pushl %esi pushl %edi /* Compute the virtual destination address */ movl 16(%esp), %edi # dest subl virt_offset, %edi /* Compute the new value of virt_offset */ movl 16(%esp), %ebp # virt_offset subl $_text, %ebp /* Fixup the gdt */ pushl $_pmcs pushl %ebp # virt_offset call set_seg_base addl $8, %esp /* Fixup gdtarg */ leal _gdt(%ebp), %eax movl %eax, gdtarg +2 /* Fixup virt_offset */ movl %ebp, virt_offset /* Load the move parameters */ movl $_text, %esi movl $_end, %ecx subl %esi, %ecx /* Move etherboot uses %esi, %edi, %ecx */ rep movsb /* Reload the gdt */ cs lgdt gdtarg /* Reload %cs */ ljmp $KERN_CODE_SEG, $1f1: /* reload other segment registers */ movl $KERN_DATA_SEG, %eax movl %eax,%ds movl %eax,%es movl %eax,%ss movl %eax,%fs movl %eax,%gs /* Restore the callee save registers */ popl %edi popl %esi popl %ebp /* return */ ret#endif /* RELOCATE */ /**************************************************************************XSTART32 - Transfer control to the kernel just loaded**************************************************************************/ .globl xstart32xstart32: /* Save the callee save registers */ movl %ebp, os_regs + 32 movl %esi, os_regs + 36 movl %edi, os_regs + 40 movl %ebx, os_regs + 44 /* save the return address */ popl %eax movl %eax, os_regs + 48 /* save the stack pointer */ movl %esp, os_regs + 52 /* Get the new destination address */ popl %ecx /* Store the physical address of xend on the stack */ movl $xend32, %ebx addl virt_offset, %ebx pushl %ebx /* Store the destination address on the stack */ pushl $FLAT_CODE_SEG pushl %ecx /* Switch to using physical addresses */ call _virt_to_phys /* Save the target stack pointer */ movl %esp, os_regs + 12(%ebp) leal os_regs(%ebp), %esp /* Store the pointer to os_regs */ movl %esp, os_regs_ptr(%ebp) /* Load my new registers */ popal movl (-32 + 12)(%esp), %esp /* Jump to the new kernel * The lret switches to a flat code segment */ lret .balign 4 .globl xend32xend32: /* Fixup %eflags */ nop cli cld /* Load %esp with &os_regs + virt_offset */ .byte 0xbc /* movl $0, %esp */os_regs_ptr: .long 0 /* Save the result registers */ addl $32, %esp pushal /* Compute virt_offset */ movl %esp, %ebp subl $os_regs, %ebp /* Load the stack pointer */ movl 52(%esp), %esp /* Enable the virtual addresses */ leal _phys_to_virt(%ebp), %eax call *%eax /* Restore the callee save registers */ movl os_regs + 32, %ebp movl os_regs + 36, %esi movl os_regs + 40, %edi movl os_regs + 44, %ebx movl os_regs + 48, %edx movl os_regs + 52, %esp /* Get the C return value */ movl os_regs + 28, %eax jmpl *%edx#ifdef CONFIG_X86_64 .arch sledgehammer/**************************************************************************XSTART_lm - Transfer control to the kernel just loaded in long mode**************************************************************************/ .globl xstart_lmxstart_lm: /* Save the callee save registers */ pushl %ebp pushl %esi pushl %edi pushl %ebx /* Switch to using physical addresses */ call _virt_to_phys /* Cache virt_offset & 0xfffff000 */ mov %ebp, %ebx andl $0xfffff000, %ebx /* Initialize the page tables */ /* Level 4 */ leal 0x23 + pgt_level3(%ebx), %eax leal pgt_level4(%ebx), %edi movl %eax, (%edi) /* Level 3 */ leal 0x23 + pgt_level2(%ebx), %eax leal pgt_level3(%ebx), %edi movl %eax, 0x00(%edi) addl $4096, %eax movl %eax, 0x08(%edi) addl $4096, %eax movl %eax, 0x10(%edi) addl $4096, %eax movl %eax, 0x18(%edi) /* Level 2 */ movl $0xe3, %eax leal pgt_level2(%ebx), %edi leal 16384(%edi), %esipgt_level2_loop: movl %eax, (%edi) addl $8, %edi addl $0x200000, %eax cmp %esi, %edi jne pgt_level2_loop /* Point at the x86_64 page tables */ leal pgt_level4(%ebx), %edi movl %edi, %cr3 /* Setup for the return from 64bit mode */ /* 64bit align the stack */ movl %esp, %ebx /* original stack pointer + 16 */ andl $0xfffffff8, %esp /* Save original stack pointer + 16 */ pushl %ebx /* Save virt_offset */ pushl %ebp /* Setup for the jmp to 64bit long mode */ leal start_lm(%ebp), %eax movl %eax, 0x00 + start_lm_addr(%ebp) movl $LM_CODE_SEG, %eax movl %eax, 0x04 + start_lm_addr(%ebp) /* Setup for the jump out of 64bit long mode */ leal end_lm(%ebp), %eax movl %eax, 0x00 + end_lm_addr(%ebp) movl $FLAT_CODE_SEG, %eax movl %eax, 0x04 + end_lm_addr(%ebp) /* Enable PAE mode */ movl %cr4, %eax orl $X86_CR4_PAE, %eax movl %eax, %cr4 /* Enable long mode */ movl $MSR_K6_EFER, %ecx rdmsr orl $EFER_LME, %eax wrmsr /* Start paging, entering 32bit compatiblity mode */ movl %cr0, %eax orl $CR0_PG, %eax movl %eax, %cr0 /* Enter 64bit long mode */ ljmp *start_lm_addr(%ebp) .code64start_lm: /* Load 64bit data segments */ movl $LM_DATA_SEG, %eax movl %eax, %ds movl %eax, %es movl %eax, %ss andq $0xffffffff, %rbx /* Get the address to jump to */ movl 20(%rbx), %edx
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -