1/* 2 * librm: a library for interfacing to real-mode code 3 * 4 * Michael Brown <mbrown@fensystems.co.uk> 5 * 6 */ 7 8FILE_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 51gdt: 52gdtr: /* The first GDT entry is unused, the GDTR can fit here. */ 53gdt_limit: .word gdt_length - 1 54gdt_base: .long 0 55 .word 0 /* padding */ 56 57 .org gdt + VIRTUAL_CS, 0 58virtual_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 63virtual_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 68physical_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 73physical_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 78real_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 83real_ds: /* 16 bit real mode data segment */ 84 .word 0xffff, 0 85 .byte 0, 0x93, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0 86 87gdt_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 104init_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 154set_seg_base: 1551: movw %ax, 2(%bx) 156 rorl $16, %eax 157 movb %al, 4(%bx) 158 movb %ah, 7(%bx) 159 roll $16, %eax 160idt_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 181real_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 2131: 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 244idtr: 245rm_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 270prot_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 3001: 301 /* Switch to real mode */ 302 movl %cr0, %eax 303 andb $0!CR0_PE, %al 304 movl %eax, %cr0 305 ljmp *p2r_jump_vector 306p2r_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 331p2r_jump_vector: 332 .word p2r_jump_target 333 .globl rm_cs 334rm_cs: .word 0 335 .globl rm_ds 336 .section ".text16.data", "aw", @progbits 337rm_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 383prot_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 4071: 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 4231: 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 480real_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 4911: 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 5071: 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 517rc_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 555rm_sp: .word 0 556 .globl rm_ss 557rm_ss: .word 0 558pm_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 577virt_offset: .long 0 578 .globl text16 579text16: .long 0 580 .globl data16 581data16: .long 0 582