david/ipxe
david
/
ipxe
Archived
1
0
Fork 0
This repository has been archived on 2020-12-06. You can view files and clone it, but cannot push or open issues or pull requests.
ipxe/src/arch/i386/core/init.S

306 lines
8.1 KiB
ArmAsm

#include "callbacks.h"
.equ CR0_PE, 1
.text
.arch i386
.section ".prefix", "ax", @progbits
#undef CODE16
#if defined(PCBIOS)
#define CODE16
#endif
/* We have two entry points: "conventional" (at the start of the file)
* and "callback" (at _entry, 2 bytes in). The "callback" entry
* should be used if the caller wishes to provide a specific opcode.
* It is equivalent to a call to in_call. Using the "conventional"
* entry point is equivalent to using the "callback" entry point with
* an opcode of EB_OPCODE_MAIN.
*
* Both entry points can be called in either 16-bit real or 32-bit
* protected mode with flat physical addresses. We detect which mode
* the processor is in and call either in_call or rm_in_call as
* appropriate. Note that the mode detection code must therefore be
* capable of doing the same thing in either mode, even though the
* machine code instructions will be interpreted differently.
*
* The decompressor will be invoked if necessary to decompress
* Etherboot before attempting to jump to it.
*/
/******************************************************************************
* Entry points and mode detection code
******************************************************************************
*/
.code32
/* "Conventional" entry point: caller provides no opcode */
.globl _start
_start:
/* Set flag to indicate conventional entry point used */
pushl $0 /* "pushw $0" in 16-bit code */
/* Fall through to "callback" entry point */
/* "Callback" entry point */
.globl _entry
_entry:
#ifdef CODE16
/* CPU mode detection code */
pushl %eax /* "pushw %ax" in 16-bit code */
pushw %ax /* "pushl %eax" in 16-bit code */
movl %cr0, %eax /* Test protected mode bit */
testb $CR0_PE, %al
popw %ax /* "popl %eax" in 16-bit code */
popl %eax /* "popw %eax" in 16-bit code */
jz rmode
#endif /* CODE16 */
/******************************************************************************
* Entered in protected mode
******************************************************************************
*/
.code32
pmode:
cmpl $0, 0(%esp) /* Conventional entry point used? */
jne 1f
/* Entered via conventional entry point: set up stack */
xchgl %eax, 4(%esp) /* %eax = return addr, store %eax */
movl %eax, 0(%esp) /* 0(%esp) = return address */
movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), %eax
xchgl %eax, 4(%esp) /* 4(%esp) = opcode, restore %eax */
1:
/* Run decompressor if necessary */
pushl %eax
movl $decompress, %eax
testl %eax, %eax
jz 1f
call decompress
1: popl %eax
/* Make in_call to Etherboot */
jmp _prefix_in_call
/******************************************************************************
* Entered in real mode
******************************************************************************
*/
#ifdef CODE16
.code16
rmode:
pushw %ax /* Padding */
pushw %bp
movw %sp, %bp
cmpw $0, 6(%bp) /* Conventional entry point used? */
jne 1f
/* Entered via conventional entry point: set up stack */
pushw %ax
movw 6(%bp), %ax
movw %ax, 2(%bp) /* Move return address down */
movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), 4(%bp)
popw %ax
popw %bp
jmp 2f
1: /* Entered via callback entry point: do nothing */
popw %bp
popw %ax
2:
/* Preserve registers */
pushw %ds
pushl %eax
/* Run decompressor if necessary. Decompressor is 32-bit
* code, so we must switch to pmode first. Save and restore
* GDT over transition to pmode.
*/
movl $decompress, %eax
testl %eax, %eax
jz 1f
pushw %ds
pushw %es
pushw %fs
pushw %gs
subw $8, %sp
pushw %bp
movw %sp, %bp
sgdt 2(%bp)
pushw %ss /* Store params for _prot_to_real */
pushw %cs
call _prefix_real_to_prot
.code32
call decompress
call _prefix_prot_to_real
.code16
popw %ax /* skip */
popw %ax /* skip */
lgdt 2(%bp)
popw %bp
addw $8, %sp
popw %gs
popw %fs
popw %es
popw %ds
1:
/* Set rm_etherboot_location */
xorl %eax, %eax
movw %cs, %ax
movw %ax, %ds
shll $4, %eax
addl $_prefix_size, %eax
movl %eax, _prefix_rm_etherboot_location
/* Restore registers */
popl %eax
popw %ds
/* Make real-mode in_call to Etherboot */
jmp _prefix_rm_in_call
#endif /* CODE16 */
/******************************************************************************
* Utility routines that can be called by the "prefix".
******************************************************************************
*/
#ifdef CODE16
/* Prelocate code: either to an area at the top of free base memory.
* Switch stacks to use the stack within the resulting
* Etherboot image.
*
* On entry, %cs:0000 must be the start of the prefix: this is used to
* locate the code to be copied.
*
* This routine takes a single word parameter: the number of bytes to
* be transferred from the old stack to the new stack (excluding the
* return address and this parameter itself, which will always be
* copied). If this value is negative, the stacks will not be
* switched.
*
* Control will "return" to the appropriate point in the relocated
* image.
*/
#define PRELOC_PRESERVE ( 20 )
#define PRELOC_OFFSET_RETADDR ( PRELOC_PRESERVE )
#define PRELOC_OFFSET_RETADDR_E ( PRELOC_OFFSET_RETADDR + 4 )
#define PRELOC_OFFSET_COPY ( PRELOC_OFFSET_RETADDR_E )
#define PRELOC_OFFSET_COPY_E ( PRELOC_OFFSET_COPY + 2 )
#define PRELOC_ALWAYS_COPY ( PRELOC_OFFSET_COPY_E )
.code16
.globl prelocate
prelocate:
/* Pad to allow for expansion of return address */
pushw %ax
/* Preserve registers */
pushaw
pushw %ds
pushw %es
/* Claim an area of base memory from the BIOS and put the
* payload there.
*/
movw $0x40, %bx
movw %bx, %es
movw %es:(0x13), %bx /* FBMS in kb to %ax */
shlw $6, %bx /* ... in paragraphs */
subw $_image_size_pgh, %bx /* Subtract space for image */
shrw $6, %bx /* Round down to nearest kb */
movw %bx, %es:(0x13) /* ...and claim memory from BIOS */
shlw $6, %bx
/* At this point %bx contains the segment address for the
* start of the image (image = prefix + runtime).
*/
/* Switch stacks */
movw %ss, %ax
movw %ax, %ds
movw %sp, %si /* %ds:si = current %ss:sp */
movw %ss:PRELOC_OFFSET_COPY(%si), %cx
testw %cx, %cx
js 1f
leaw _stack_offset_pgh(%bx), %ax /* %ax = new %ss */
movw %ax, %es
movw $_stack_size, %di
addw $PRELOC_ALWAYS_COPY, %cx
subw %cx, %di /* %es:di = new %ss:sp */
movw %ax, %ss /* Set new %ss:sp */
movw %di, %sp
cld
rep movsb /* Copy stack contents */
1:
/* Do the image copy backwards, since if there's overlap with
* a forward copy then it means we're going to get trashed
* during the copy anyway...
*/
pushal /* Preserve 32-bit registers */
movw %bx, %es /* Destination base for copy */
pushw %cs
popw %ds /* Source base for copy */
movl $_verbatim_size-1, %ecx /* Offset to last byte */
movl %ecx, %esi
movl %ecx, %edi
incl %ecx /* Length */
std /* Backwards copy of binary */
ADDR32 rep movsb
cld
popal /* Restore 32-bit registers */
/* Store (%bx<<4) as image_basemem to be picked up by
* basemem.c. Also store image_size, since there's no other
* way that we can later know how much memory we allocated.
* (_zfile_size is unavailable when rt2 is linked).
*/
pushl %eax
xorl %eax, %eax
movw %bx, %ax
shll $4, %eax
movl %eax, %es:_prefix_image_basemem
movl $_image_size, %es:_prefix_image_basemem_size
popl %eax
/* Expand original near return address into far return to new
* code location.
*/
movw %sp, %bp
xchgw %bx, (PRELOC_OFFSET_RETADDR+2)(%bp)
movw %bx, (PRELOC_OFFSET_RETADDR+0)(%bp)
/* Restore registers and return */
popw %es
popw %ds
popaw
lret /* Jump to relocated code */
/* Utility routine to free base memory allocated by prelocate.
* Ensure that said memory is not in use (e.g. for the CPU
* stack) before calling this routine.
*/
.globl deprelocate
deprelocate:
/* Claim an area of base memory from the BIOS and put the
* payload there.
*/
pushw %ax
pushw %es
movw $0x40, %ax
movw %ax, %es
movw %es:(0x13), %ax /* FBMS in kb to %ax */
shlw $6, %ax /* ... in paragraphs */
addw $_image_size_pgh+0x40-1, %ax /* Add space for image and... */
shrw $6, %ax /* ...round up to nearest kb */
movw %ax, %es:(0x13) /* Give memory back to BIOS */
popw %es
popw %ax
ret
#endif /* CODE16 */