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 |
---|