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