Greg Hartman | 76d05dc | 2016-11-23 15:51:27 -0800 | [diff] [blame] | 1 | /* |
| 2 | * librm: a library for interfacing to real-mode code |
| 3 | * |
| 4 | * Michael Brown <mbrown@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 |