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