Home | History | Annotate | Download | only in transitions
      1 /*
      2  * librm: a library for interfacing to real-mode code
      3  *
      4  * Michael Brown <mbrown (at) fensystems.co.uk>
      5  *
      6  */
      7 
      8 FILE_LICENCE ( GPL2_OR_LATER )
      9 
     10 /* Drag in local definitions */
     11 #include "librm.h"
     12 
     13 /* For switches to/from protected mode */
     14 #define CR0_PE 1
     15 
     16 /* Size of various C data structures */
     17 #define SIZEOF_I386_SEG_REGS	12
     18 #define SIZEOF_I386_REGS	32
     19 #define SIZEOF_REAL_MODE_REGS	( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
     20 #define SIZEOF_I386_FLAGS	4
     21 #define SIZEOF_I386_ALL_REGS	( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS )
     22 
     23 	.arch i386
     24 
     25 /****************************************************************************
     26  * Global descriptor table
     27  *
     28  * Call init_librm to set up the GDT before attempting to use any
     29  * protected-mode code.
     30  *
     31  * Define FLATTEN_REAL_MODE if you want to use so-called "flat real
     32  * mode" with 4GB limits instead.
     33  *
     34  * NOTE: This must be located before prot_to_real, otherwise gas
     35  * throws a "can't handle non absolute segment in `ljmp'" error due to
     36  * not knowing the value of REAL_CS when the ljmp is encountered.
     37  *
     38  * Note also that putting ".word gdt_end - gdt - 1" directly into
     39  * gdt_limit, rather than going via gdt_length, will also produce the
     40  * "non absolute segment" error.  This is most probably a bug in gas.
     41  ****************************************************************************
     42  */
     43 
     44 #ifdef FLATTEN_REAL_MODE
     45 #define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
     46 #else
     47 #define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
     48 #endif
     49 	.section ".data16", "aw", @progbits
     50 	.align 16
     51 gdt:
     52 gdtr:		/* The first GDT entry is unused, the GDTR can fit here. */
     53 gdt_limit:		.word gdt_length - 1
     54 gdt_base:		.long 0
     55 			.word 0 /* padding */
     56 
     57 	.org	gdt + VIRTUAL_CS, 0
     58 virtual_cs:	/* 32 bit protected mode code segment, virtual addresses */
     59 	.word	0xffff, 0
     60 	.byte	0, 0x9f, 0xcf, 0
     61 
     62 	.org	gdt + VIRTUAL_DS, 0
     63 virtual_ds:	/* 32 bit protected mode data segment, virtual addresses */
     64 	.word	0xffff, 0
     65 	.byte	0, 0x93, 0xcf, 0
     66 
     67 	.org	gdt + PHYSICAL_CS, 0
     68 physical_cs:	/* 32 bit protected mode code segment, physical addresses */
     69 	.word	0xffff, 0
     70 	.byte	0, 0x9f, 0xcf, 0
     71 
     72 	.org	gdt + PHYSICAL_DS, 0
     73 physical_ds:	/* 32 bit protected mode data segment, physical addresses */
     74 	.word	0xffff, 0
     75 	.byte	0, 0x93, 0xcf, 0
     76 
     77 	.org	gdt + REAL_CS, 0
     78 real_cs: 	/* 16 bit real mode code segment */
     79 	.word	0xffff, 0
     80 	.byte	0, 0x9b, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0
     81 
     82 	.org	gdt + REAL_DS
     83 real_ds:	/* 16 bit real mode data segment */
     84 	.word	0xffff, 0
     85 	.byte	0, 0x93, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0
     86 
     87 gdt_end:
     88 	.equ	gdt_length, gdt_end - gdt
     89 
     90 /****************************************************************************
     91  * init_librm (real-mode far call, 16-bit real-mode far return address)
     92  *
     93  * Initialise the GDT ready for transitions to protected mode.
     94  *
     95  * Parameters:
     96  *   %cs : .text16 segment
     97  *   %ds : .data16 segment
     98  *   %edi : Physical base of protected-mode code (virt_offset)
     99  ****************************************************************************
    100  */
    101 	.section ".text16", "ax", @progbits
    102 	.code16
    103 	.globl init_librm
    104 init_librm:
    105 	/* Preserve registers */
    106 	pushl	%eax
    107 	pushl	%ebx
    108 
    109 	/* Store _virt_offset and set up virtual_cs and virtual_ds segments */
    110 	movl	%edi, %eax
    111 	movw	$virtual_cs, %bx
    112 	call	set_seg_base
    113 	movw	$virtual_ds, %bx
    114 	call	set_seg_base
    115 	movl	%edi, _virt_offset
    116 
    117 	/* Negate virt_offset */
    118 	negl	%edi
    119 
    120 	/* Store rm_cs and _text16, set up real_cs segment */
    121 	xorl	%eax, %eax
    122 	movw	%cs, %ax
    123 	movw	%ax, rm_cs
    124 	shll	$4, %eax
    125 	movw	$real_cs, %bx
    126 	call	set_seg_base
    127 	addr32 leal	(%eax, %edi), %ebx
    128 	movl	%ebx, _text16
    129 
    130 	/* Store rm_ds and _data16, set up real_ds segment */
    131 	xorl	%eax, %eax
    132 	movw	%ds, %ax
    133 	movw	%ax, %cs:rm_ds
    134 	shll	$4, %eax
    135 	movw	$real_ds, %bx
    136 	call	set_seg_base
    137 	addr32 leal	(%eax, %edi), %ebx
    138 	movl	%ebx, _data16
    139 
    140 	/* Set GDT and IDT base */
    141 	movl	%eax, gdt_base
    142 	addl	$gdt, gdt_base
    143 	call	idt_init
    144 
    145 	/* Restore registers */
    146 	negl	%edi
    147 	popl	%ebx
    148 	popl	%eax
    149 	lret
    150 
    151 	.section ".text16", "ax", @progbits
    152 	.code16
    153 	.weak idt_init
    154 set_seg_base:
    155 1:	movw	%ax, 2(%bx)
    156 	rorl	$16, %eax
    157 	movb	%al, 4(%bx)
    158 	movb	%ah, 7(%bx)
    159 	roll	$16, %eax
    160 idt_init: /* Reuse the return opcode here */
    161 	ret
    162 
    163 /****************************************************************************
    164  * real_to_prot (real-mode near call, 32-bit virtual return address)
    165  *
    166  * Switch from 16-bit real-mode to 32-bit protected mode with virtual
    167  * addresses.  The real-mode %ss:sp is stored in rm_ss and rm_sp, and
    168  * the protected-mode %esp is restored from the saved pm_esp.
    169  * Interrupts are disabled.  All other registers may be destroyed.
    170  *
    171  * The return address for this function should be a 32-bit virtual
    172  * address.
    173  *
    174  * Parameters:
    175  *   %ecx : number of bytes to move from RM stack to PM stack
    176  *
    177  ****************************************************************************
    178  */
    179 	.section ".text16", "ax", @progbits
    180 	.code16
    181 real_to_prot:
    182 	/* Make sure we have our data segment available */
    183 	movw	%cs:rm_ds, %ax
    184 	movw	%ax, %ds
    185 
    186 	/* Add _virt_offset, _text16 and _data16 to stack to be
    187 	 * copied, and also copy the return address.
    188 	 */
    189 	pushl	_virt_offset
    190 	pushl	_text16
    191 	pushl	_data16
    192 	addw	$16, %cx /* %ecx must be less than 64kB anyway */
    193 
    194 	/* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */
    195 	xorl	%ebp, %ebp
    196 	movw	%ss, %bp
    197 	movzwl	%sp, %edx
    198 	movl	%ebp, %eax
    199 	shll	$4, %eax
    200 	addr32 leal (%eax,%edx), %esi
    201 	subl	_virt_offset, %esi
    202 
    203 	/* Switch to protected mode */
    204 	cli
    205 	data32 lgdt gdtr
    206 	data32 lidt idtr
    207 	movl	%cr0, %eax
    208 	orb	$CR0_PE, %al
    209 	movl	%eax, %cr0
    210 	data32 ljmp	$VIRTUAL_CS, $1f
    211 	.section ".text", "ax", @progbits
    212 	.code32
    213 1:
    214 	/* Set up protected-mode data segments and stack pointer */
    215 	movw	$VIRTUAL_DS, %ax
    216 	movw	%ax, %ds
    217 	movw	%ax, %es
    218 	movw	%ax, %fs
    219 	movw	%ax, %gs
    220 	movw	%ax, %ss
    221 	movl	pm_esp, %esp
    222 
    223 	/* Record real-mode %ss:sp (after removal of data) */
    224 	movw	%bp, rm_ss
    225 	addl	%ecx, %edx
    226 	movw	%dx, rm_sp
    227 
    228 	/* Move data from RM stack to PM stack */
    229 	subl	%ecx, %esp
    230 	movl	%esp, %edi
    231 	rep movsb
    232 
    233 	/* Publish virt_offset, text16 and data16 for PM code to use */
    234 	popl	data16
    235 	popl	text16
    236 	popl	virt_offset
    237 
    238 	/* Return to virtual address */
    239 	ret
    240 
    241 	/* Default IDTR with no interrupts */
    242 	.section ".data16", "aw", @progbits
    243 	.weak idtr
    244 idtr:
    245 rm_idtr:
    246 	.word 0xffff /* limit */
    247 	.long 0 /* base */
    248 
    249 /****************************************************************************
    250  * prot_to_real (protected-mode near call, 32-bit real-mode return address)
    251  *
    252  * Switch from 32-bit protected mode with virtual addresses to 16-bit
    253  * real mode.  The protected-mode %esp is stored in pm_esp and the
    254  * real-mode %ss:sp is restored from the saved rm_ss and rm_sp.  The
    255  * high word of the real-mode %esp is set to zero.  All real-mode data
    256  * segment registers are loaded from the saved rm_ds.  Interrupts are
    257  * *not* enabled, since we want to be able to use prot_to_real in an
    258  * ISR.  All other registers may be destroyed.
    259  *
    260  * The return address for this function should be a 32-bit (sic)
    261  * real-mode offset within .code16.
    262  *
    263  * Parameters:
    264  *   %ecx : number of bytes to move from PM stack to RM stack
    265  *
    266  ****************************************************************************
    267  */
    268 	.section ".text", "ax", @progbits
    269 	.code32
    270 prot_to_real:
    271 	/* Add return address to data to be moved to RM stack */
    272 	addl	$4, %ecx
    273 
    274 	/* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */
    275 	movzwl	rm_ss, %ebp
    276 	movzwl	rm_sp, %edx
    277 	subl	%ecx, %edx
    278 	movl	%ebp, %eax
    279 	shll	$4, %eax
    280 	leal	(%eax,%edx), %edi
    281 	subl	virt_offset, %edi
    282 
    283 	/* Move data from PM stack to RM stack */
    284 	movl	%esp, %esi
    285 	rep movsb
    286 
    287 	/* Record protected-mode %esp (after removal of data) */
    288 	movl	%esi, pm_esp
    289 
    290 	/* Load real-mode segment limits */
    291 	movw	$REAL_DS, %ax
    292 	movw	%ax, %ds
    293 	movw	%ax, %es
    294 	movw	%ax, %fs
    295 	movw	%ax, %gs
    296 	movw	%ax, %ss
    297 	ljmp	$REAL_CS, $1f
    298 	.section ".text16", "ax", @progbits
    299 	.code16
    300 1:
    301 	/* Switch to real mode */
    302 	movl	%cr0, %eax
    303 	andb	$0!CR0_PE, %al
    304 	movl	%eax, %cr0
    305 	ljmp	*p2r_jump_vector
    306 p2r_jump_target:
    307 
    308 	/* Set up real-mode data segments and stack pointer */
    309 	movw	%cs:rm_ds, %ax
    310 	movw	%ax, %ds
    311 	movw	%ax, %es
    312 	movw	%ax, %fs
    313 	movw	%ax, %gs
    314 	movw	%bp, %ss
    315 	movl	%edx, %esp
    316 
    317 	/* Reset IDTR to the real-mode defaults */
    318 	data32 lidt rm_idtr
    319 
    320 	/* Return to real-mode address */
    321 	data32 ret
    322 
    323 
    324 	/* Real-mode code and data segments.  Assigned by the call to
    325 	 * init_librm.  rm_cs doubles as the segment part of the jump
    326 	 * vector used by prot_to_real.  rm_ds is located in .text16
    327 	 * rather than .data16 because code needs to be able to locate
    328 	 * the data segment.
    329 	 */
    330 	.section ".data16", "aw", @progbits
    331 p2r_jump_vector:
    332 	.word	p2r_jump_target
    333 	.globl rm_cs
    334 rm_cs:	.word 0
    335 	.globl rm_ds
    336 	.section ".text16.data", "aw", @progbits
    337 rm_ds:	.word 0
    338 
    339 /****************************************************************************
    340  * prot_call (real-mode far call, 16-bit real-mode far return address)
    341  *
    342  * Call a specific C function in the protected-mode code.  The
    343  * prototype of the C function must be
    344  *   void function ( struct i386_all_regs *ix86 );
    345  * ix86 will point to a struct containing the real-mode registers
    346  * at entry to prot_call.
    347  *
    348  * All registers will be preserved across prot_call(), unless the C
    349  * function explicitly overwrites values in ix86.  Interrupt status
    350  * and GDT will also be preserved.  Gate A20 will be enabled.
    351  *
    352  * Note that prot_call() does not rely on the real-mode stack
    353  * remaining intact in order to return, since everything relevant is
    354  * copied to the protected-mode stack for the duration of the call.
    355  * In particular, this means that a real-mode prefix can make a call
    356  * to main() which will return correctly even if the prefix's stack
    357  * gets vapourised during the Etherboot run.  (The prefix cannot rely
    358  * on anything else on the stack being preserved, so should move any
    359  * critical data to registers before calling main()).
    360  *
    361  * Parameters:
    362  *   function : virtual address of protected-mode function to call
    363  *
    364  * Example usage:
    365  *	pushl	$pxe_api_call
    366  *	call	prot_call
    367  *	addw	$4, %sp
    368  * to call in to the C function
    369  *      void pxe_api_call ( struct i386_all_regs *ix86 );
    370  ****************************************************************************
    371  */
    372 
    373 #define PC_OFFSET_GDT ( 0 )
    374 #define PC_OFFSET_IDT ( PC_OFFSET_GDT + 8 /* pad to 8 to keep alignment */ )
    375 #define PC_OFFSET_IX86 ( PC_OFFSET_IDT + 8 /* pad to 8 to keep alignment */ )
    376 #define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS )
    377 #define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
    378 #define PC_OFFSET_END ( PC_OFFSET_FUNCTION + 4 )
    379 
    380 	.section ".text16", "ax", @progbits
    381 	.code16
    382 	.globl prot_call
    383 prot_call:
    384 	/* Preserve registers, flags and GDT on external RM stack */
    385 	pushfl
    386 	pushal
    387 	pushw	%gs
    388 	pushw	%fs
    389 	pushw	%es
    390 	pushw	%ds
    391 	pushw	%ss
    392 	pushw	%cs
    393 	subw	$16, %sp
    394 	movw	%sp, %bp
    395 	sidt	8(%bp)
    396 	sgdt	(%bp)
    397 
    398 	/* For sanity's sake, clear the direction flag as soon as possible */
    399 	cld
    400 
    401 	/* Switch to protected mode and move register dump to PM stack */
    402 	movl	$PC_OFFSET_END, %ecx
    403 	pushl	$1f
    404 	jmp	real_to_prot
    405 	.section ".text", "ax", @progbits
    406 	.code32
    407 1:
    408 	/* Set up environment expected by C code */
    409 	call	gateA20_set
    410 
    411 	/* Call function */
    412 	leal	PC_OFFSET_IX86(%esp), %eax
    413 	pushl	%eax
    414 	call	*(PC_OFFSET_FUNCTION+4)(%esp)
    415 	popl	%eax /* discard */
    416 
    417 	/* Switch to real mode and move register dump back to RM stack */
    418 	movl	$PC_OFFSET_END, %ecx
    419 	pushl	$1f
    420 	jmp	prot_to_real
    421 	.section ".text16", "ax", @progbits
    422 	.code16
    423 1:
    424 	/* Reload GDT and IDT, restore registers and flags and return */
    425 	movw	%sp, %bp
    426 	data32 lgdt (%bp)
    427 	data32 lidt 8(%bp)
    428 	addw	$20, %sp /* also skip %cs and %ss */
    429 	popw	%ds
    430 	popw	%es
    431 	popw	%fs
    432 	popw	%gs
    433 	popal
    434 	/* popal skips %esp.  We therefore want to do "movl -20(%sp),
    435 	 * %esp", but -20(%sp) is not a valid 80386 expression.
    436 	 * Fortunately, prot_to_real() zeroes the high word of %esp, so
    437 	 * we can just use -20(%esp) instead.
    438 	 */
    439 	addr32 movl -20(%esp), %esp
    440 	popfl
    441 	lret
    442 
    443 /****************************************************************************
    444  * real_call (protected-mode near call, 32-bit virtual return address)
    445  *
    446  * Call a real-mode function from protected-mode code.
    447  *
    448  * The non-segment register values will be passed directly to the
    449  * real-mode code.  The segment registers will be set as per
    450  * prot_to_real.  The non-segment register values set by the real-mode
    451  * function will be passed back to the protected-mode caller.  A
    452  * result of this is that this routine cannot be called directly from
    453  * C code, since it clobbers registers that the C ABI expects the
    454  * callee to preserve.  Gate A20 will *not* be automatically
    455  * re-enabled.  Since we always run from an even megabyte of memory,
    456  * we are guaranteed to return successfully to the protected-mode
    457  * code, which should then call gateA20_set() if it suspects that gate
    458  * A20 may have been disabled.  Note that enabling gate A20 is a
    459  * potentially slow operation that may also cause keyboard input to be
    460  * lost; this is why it is not done automatically.
    461  *
    462  * librm.h defines a convenient macro REAL_CODE() for using real_call.
    463  * See librm.h and realmode.h for details and examples.
    464  *
    465  * Parameters:
    466  *   (32-bit) near pointer to real-mode function to call
    467  *
    468  * Returns: none
    469  ****************************************************************************
    470  */
    471 
    472 #define RC_OFFSET_PRESERVE_REGS ( 0 )
    473 #define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + SIZEOF_I386_REGS )
    474 #define RC_OFFSET_FUNCTION ( RC_OFFSET_RETADDR + 4 )
    475 #define RC_OFFSET_END ( RC_OFFSET_FUNCTION + 4 )
    476 
    477 	.section ".text", "ax", @progbits
    478 	.code32
    479 	.globl real_call
    480 real_call:
    481 	/* Create register dump and function pointer copy on PM stack */
    482 	pushal
    483 	pushl	RC_OFFSET_FUNCTION(%esp)
    484 
    485 	/* Switch to real mode and move register dump to RM stack  */
    486 	movl	$( RC_OFFSET_RETADDR + 4 /* function pointer copy */ ), %ecx
    487 	pushl	$1f
    488 	jmp	prot_to_real
    489 	.section ".text16", "ax", @progbits
    490 	.code16
    491 1:
    492 	/* Call real-mode function */
    493 	popl	rc_function
    494 	popal
    495 	call	*rc_function
    496 	pushal
    497 
    498 	/* For sanity's sake, clear the direction flag as soon as possible */
    499 	cld
    500 
    501 	/* Switch to protected mode and move register dump back to PM stack */
    502 	movl	$RC_OFFSET_RETADDR, %ecx
    503 	pushl	$1f
    504 	jmp	real_to_prot
    505 	.section ".text", "ax", @progbits
    506 	.code32
    507 1:
    508 	/* Restore registers and return */
    509 	popal
    510 	ret
    511 
    512 
    513 	/* Function vector, used because "call xx(%sp)" is not a valid
    514 	 * 16-bit expression.
    515 	 */
    516 	.section ".data16", "aw", @progbits
    517 rc_function:	.word 0, 0
    518 
    519 /****************************************************************************
    520  * Stored real-mode and protected-mode stack pointers
    521  *
    522  * The real-mode stack pointer is stored here whenever real_to_prot
    523  * is called and restored whenever prot_to_real is called.  The
    524  * converse happens for the protected-mode stack pointer.
    525  *
    526  * Despite initial appearances this scheme is, in fact re-entrant,
    527  * because program flow dictates that we always return via the point
    528  * we left by.  For example:
    529  *    PXE API call entry
    530  *  1   real => prot
    531  *        ...
    532  *        Print a text string
    533  *	    ...
    534  *  2       prot => real
    535  *            INT 10
    536  *  3       real => prot
    537  *	    ...
    538  *        ...
    539  *  4   prot => real
    540  *    PXE API call exit
    541  *
    542  * At point 1, the RM mode stack value, say RPXE, is stored in
    543  * rm_ss,sp.  We want this value to still be present in rm_ss,sp when
    544  * we reach point 4.
    545  *
    546  * At point 2, the RM stack value is restored from RPXE.  At point 3,
    547  * the RM stack value is again stored in rm_ss,sp.  This *does*
    548  * overwrite the RPXE that we have stored there, but it's the same
    549  * value, since the code between points 2 and 3 has managed to return
    550  * to us.
    551  ****************************************************************************
    552  */
    553 	.section ".data", "aw", @progbits
    554 	.globl rm_sp
    555 rm_sp:	.word 0
    556 	.globl rm_ss
    557 rm_ss:	.word 0
    558 pm_esp:	.long _estack
    559 
    560 /****************************************************************************
    561  * Virtual address offsets
    562  *
    563  * These are used by the protected-mode code to map between virtual
    564  * and physical addresses, and to access variables in the .text16 or
    565  * .data16 segments.
    566  ****************************************************************************
    567  */
    568 	/* Internal copies, created by init_librm (which runs in real mode) */
    569 	.section ".data16", "aw", @progbits
    570 _virt_offset:	.long 0
    571 _text16:	.long 0
    572 _data16:	.long 0
    573 
    574 	/* Externally-visible copies, created by real_to_prot */
    575 	.section ".data", "aw", @progbits
    576 	.globl virt_offset
    577 virt_offset:	.long 0
    578 	.globl text16
    579 text16:		.long 0
    580 	.globl data16
    581 data16:		.long 0
    582