/
realmode_asm.S
695 lines (601 loc) · 18.4 KB
/
realmode_asm.S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
/* Real-mode interface: assembly-language portions.
*
* Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
*/
#include "realmode.h"
#include "callbacks.h"
#if 1 /* CODE16 */
#define BOCHSBP xchgw %bx,%bx
#define NUM_PUSHA_REGS (8)
#define NUM_SEG_REGS (6)
.text
.arch i386
.section ".text16.nocompress", "ax", @progbits
.code16
.equ CR0_PE,1
#ifdef GAS291
#define DATA32 data32;
#define ADDR32 addr32;
#define LJMPI(x) ljmp x
#else
#define DATA32 data32
#define ADDR32 addr32
/* newer GAS295 require #define LJMPI(x) ljmp *x */
#define LJMPI(x) ljmp x
#endif
/****************************************************************************
* REAL-MODE CALLBACK INTERFACE
*
* This must be copied down to base memory in order for external
* programs to be able to make calls in to Etherboot. Store the
* current physical address of Etherboot (i.e. virt_to_phys(_text)) in
* (uint32_t)rm_etherboot_location, then copy
* (uint16_t)rm_callback_interface_size bytes starting at
* &((void)rm_callback_interface).
*
* There are two defined entry points:
* Offset RM_IN_CALL = 0 Near call entry point
* Offset RM_IN_CALL_FAR = 2 Far call entry point
*
* Note that the routines _prot_to_real and _real_to_prot double as
* trampoline fragments for external calls (calls from Etherboot to
* real-mode code). _prot_to_real does not automatically re-enable
* interrupts; this is to allow for the potential of using Etherboot
* code as an ISR. _real_to_prot does automatically disable
* interrupts, since we don't have a protected-mode IDT.
****************************************************************************
*/
.globl rm_callback_interface
.code16
rm_callback_interface:
.globl _rm_in_call
_rm_in_call:
jmp _real_in_call
.globl _rm_in_call_far
_rm_in_call_far:
jmp _real_in_call_far
/****************************************************************************
* _real_in_call
*
* Parameters:
* 16-bit real-mode near/far return address (implicit from [l]call
* to routine) Other parameters as for _in_call_far().
*
* This routine will convert the 16-bit real-mode far return address
* to a 32-bit real-mode far return address, switch to protected mode
* using _real_to_prot and call in to _in_call_far.
****************************************************************************
*/
#define RIC_PRESERVE ( 8 )
#define RIC_OFFSET_CALLADDR ( RIC_PRESERVE )
#define RIC_OFFSET_CALLADDR_E ( RIC_OFFSET_CALLADDR + 4 )
#define RIC_OFFSET_CONTADDR ( RIC_OFFSET_CALLADDR_E )
#define RIC_OFFSET_CONTADDR_E ( RIC_OFFSET_CONTADDR + 4 )
#define RIC_OFFSET_OPCODE ( RIC_OFFSET_CONTADDR_E )
#define RIC_OFFSET_OPCODE_E ( RIC_OFFSET_OPCODE + 4 )
#define RIC_OFFSET_SEG_REGS ( RIC_OFFSET_OPCODE_E )
#define RIC_OFFSET_SEG_REGS_E ( RIC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) )
#define RIC_OFFSET_PAD ( RIC_OFFSET_SEG_REGS_E )
#define RIC_OFFSET_PAD_E ( RIC_OFFSET_PAD + 2 )
#define RIC_OFFSET_FLAGS ( RIC_OFFSET_PAD_E )
#define RIC_OFFSET_FLAGS_E ( RIC_OFFSET_FLAGS + 2 )
#define RIC_OFFSET_RETADDR ( RIC_OFFSET_FLAGS_E )
#define RIC_OFFSET_RETADDR_E ( RIC_OFFSET_RETADDR + 4 )
#define RIC_OFFSET_ORIG_OPCODE ( RIC_OFFSET_RETADDR_E )
#define RIC_INSERT_LENGTH ( RIC_OFFSET_OPCODE_E - RIC_OFFSET_CALLADDR )
.code16
_real_in_call:
/* Expand near return address to far return address
*/
pushw %ax /* Extend stack, store %ax */
pushfw
pushw %bp
movw %sp, %bp
movw %cs, %ax
xchgw %ax, 6(%bp)
xchgw %ax, 4(%bp) /* also restores %ax */
popw %bp
popfw
/* Fall through to _real_in_call_far */
_real_in_call_far:
/* Store flags and pad */
pushfw
pushw %ax
/* Store segment registers. Order matches that of seg_regs_t */
pushw %gs
pushw %fs
pushw %es
pushw %ds
pushw %ss
pushw %cs
/* Switch to protected mode */
call _real_to_prot
.code32
/* Allow space for expanded stack */
subl $RIC_INSERT_LENGTH, %esp
/* Store temporary registers */
pushl %ebp
pushl %eax
/* Copy opcode, set EB_CALL_FROM_REAL_MODE and EP_SKIP_OPCODE.
* Copy it because _in_call() and i386_in_call() expect it at
* a fixed position, not as part of the va_list.
*/
movl RIC_OFFSET_ORIG_OPCODE(%esp), %eax
orl $(EB_CALL_FROM_REAL_MODE|EB_SKIP_OPCODE), %eax
movl %eax, RIC_OFFSET_OPCODE(%esp)
/* Set up call and return addresses */
call 1f
1: popl %ebp
subl $1b, %ebp /* %ebp = offset */
movl rm_etherboot_location(%ebp), %eax /* Etherboot phys addr */
subl $_text, %eax
addl $_in_call, %eax /* _in_call phys addr */
movl %eax, RIC_OFFSET_CALLADDR(%esp)
leal 2f(%ebp), %eax /* continuation address */
movl %eax, RIC_OFFSET_CONTADDR(%esp)
/* Restore temporary registers */
popl %eax
popl %ebp
/* Call to _in_call */
ret
/* opcode will be popped automatically thanks to EB_SKIP_OPCODE */
2: /* Continuation point */
call _prot_to_real /* Return to real mode */
/* Note: the first two words of our segment register store
* happens to be exactly what we need to pass as parameters to
* _prot_to_real.
*/
.code16
popw %ds /* Restore segment registers */
popw %ds /* (skip cs&ss since these */
popw %ds /* have already been set by */
popw %es /* _prot_to_real */
popw %fs
popw %gs
addw $2, %sp /* skip pad */
/* Check for EB_SKIP_OPCODE */
pushw %bp
movw %sp, %bp
testl $EB_SKIP_OPCODE, 6(%bp)
popw %bp
jnz 1f
/* Normal return */
popfw /* Restore interrupt status */
lret /* Back to caller */
1: /* Return and skip opcode */
popfw
lret $4
/****************************************************************************
* rm_etherboot_location: the current physical location of Etherboot.
* Needed so that real-mode callback routines can locate Etherboot.
****************************************************************************
*/
.globl rm_etherboot_location
rm_etherboot_location: .long 0
/****************************************************************************
* _prot_to_real_prefix
*
* Trampoline fragment. Switch from 32-bit protected mode with flat
* physical addresses to 16-bit real mode. Store registers in the
* trampoline for restoration by _real_to_prot_suffix. Switch to
* stack in base memory.
****************************************************************************
*/
.globl _prot_to_real_prefix
.code32
_prot_to_real_prefix:
/* Registers to preserve */
pushl %ebx
pushl %esi
pushl %edi
pushl %ebp
/* Calculate offset */
call 1f
1: popl %ebp
subl $1b, %ebp /* %ebp = offset for labels in p2r*/
/* Preserve registers and return address in r2p_params */
movl p2r_r2p_params(%ebp), %ebx
subl $r2p_params, %ebx /* %ebx = offset for labels in r2p */
popl r2p_ebp(%ebx)
popl r2p_edi(%ebx)
popl r2p_esi(%ebx)
popl r2p_ebx(%ebx)
popl r2p_ret_addr(%ebx)
movl %esp, r2p_esp(%ebx)
/* Switch stacks */
movl p2r_esp(%ebp), %esp
/* Switch to real mode */
pushl p2r_segments(%ebp)
call _prot_to_real
.code16
addw $4, %sp
/* Fall through to next trampoline fragment */
jmp _prot_to_real_prefix_end
/****************************************************************************
* _prot_to_real
*
* Switch from 32-bit protected mode with flat physical addresses to
* 16-bit real mode. Stack and code must be in base memory when
* called. %cs, %ss, %eip, %esp are changed to real-mode values,
* other segment registers are destroyed, all other registers are
* preserved. Interrupts are *not* enabled.
*
* Parameters:
* %cs Real-mode code segment (word)
* %ss Real-mode stack segment (word)
****************************************************************************
*/
#define P2R_PRESERVE ( 12 )
#define P2R_OFFSET_RETADDR ( P2R_PRESERVE )
#define P2R_OFFSET_RETADDR_E ( P2R_OFFSET_RETADDR + 4 )
#define P2R_OFFSET_CS ( P2R_OFFSET_RETADDR_E )
#define P2R_OFFSET_CS_E ( P2R_OFFSET_CS + 2 )
#define P2R_OFFSET_SS ( P2R_OFFSET_CS_E )
#define P2R_OFFSET_SS_E ( P2R_OFFSET_SS + 2 )
.globl _prot_to_real
.code32
_prot_to_real:
/* Preserve registers */
pushl %ebp
pushl %ebx
pushl %eax
/* Calculate offset */
call 1f
1: popl %ebp
subl $1b, %ebp /* %ebp = offset for labels in p2r*/
/* Set up GDT with real-mode limits and appropriate bases for
* real-mode %cs and %ss. Set up protected-mode continuation
* point on stack.
*/
/* Fixup GDT */
leal p2r_gdt(%ebp), %eax
movl %eax, p2r_gdt_addr(%ebp)
/* Calculate CS base address: set GDT code segment, adjust
* return address, set up continuation address on stack.
*/
movzwl P2R_OFFSET_CS(%esp), %eax
shll $4, %eax
/* Change return address to real-mode far address */
subl %eax, P2R_OFFSET_RETADDR(%esp)
movl %eax, %ebx
shrl $4, %ebx
movw %bx, (P2R_OFFSET_RETADDR+2)(%esp)
/* First real mode address */
movl %eax, %ebx
shrl $4, %ebx
pushw %bx
leal 3f(%ebp), %ebx
subl %eax, %ebx
pushw %bx
/* Continuation address */
pushl $(p2r_rmcs - p2r_gdt)
leal 2f(%ebp), %ebx
subl %eax, %ebx
pushl %ebx
/* Code segment in GDT */
movw %ax, (p2r_rmcs+2)(%ebp)
shrl $16, %eax /* Remainder of cs base addr */
movb %al, (p2r_rmcs+4)(%ebp)
movb %ah, (p2r_rmcs+7)(%ebp)
/* Calculate SS base address: set GDT data segment, retain to
* use for adjusting %esp.
*/
movzwl (12+P2R_OFFSET_SS)(%esp), %eax /* Set ss base address */
shll $4, %eax
movw %ax, (p2r_rmds+2)(%ebp)
movl %eax, %ebx
shrl $16, %ebx
movb %bl, (p2r_rmds+4)(%ebp)
movb %bh, (p2r_rmds+7)(%ebp)
/* Load GDT */
lgdt p2r_gdt(%ebp)
/* Reload all segment registers and adjust %esp */
movw $(p2r_rmds - p2r_gdt), %bx /* Pmode DS */
movw %bx, %ss
subl %eax, %esp /* %esp now less than 0x10000 */
movw %bx, %ds
movw %bx, %es
movw %bx, %fs
movw %bx, %gs
lret /* %cs:eip */
2: /* Segment registers now have 16-bit limits. */
.code16
/* Switch to real mode */
movl %cr0, %ebx
andb $0!CR0_PE, %bl
movl %ebx, %cr0
/* Make intersegment jmp to flush the processor pipeline
* and reload %cs:%eip (to clear upper 16 bits of %eip).
*/
lret
3:
/* Load real-mode segment value to %ss. %sp already OK */
shrl $4, %eax
movw %ax, %ss
/* Restore registers */
popl %eax
popl %ebx
popl %ebp
/* Return to caller in real-mode */
lret
#ifdef FLATTEN_REAL_MODE
#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
#else
#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
#endif
p2r_gdt:
p2r_gdtarg:
p2r_gdt_limit: .word p2r_gdt_end - p2r_gdt - 1
p2r_gdt_addr: .long 0
p2r_gdt_padding: .word 0
p2r_rmcs:
/* 16 bit real mode code segment */
.word 0xffff,(0&0xffff)
.byte (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
p2r_rmds:
/* 16 bit real mode data segment */
.word 0xffff,(0&0xffff)
.byte (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
p2r_gdt_end:
/* This is the end of the trampoline prefix code. When used
* as a prefix, fall through to the following code in the
* trampoline.
*/
p2r_params: /* Structure must match prot_to_real_params_t in realmode.h */
p2r_esp: .long 0
p2r_segments:
p2r_cs: .word 0
p2r_ss: .word 0
p2r_r2p_params: .long 0
.globl _prot_to_real_prefix_end
_prot_to_real_prefix_end:
.globl _prot_to_real_prefix_size
.equ _prot_to_real_prefix_size, _prot_to_real_prefix_end - _prot_to_real_prefix
.globl prot_to_real_prefix_size
prot_to_real_prefix_size:
.word _prot_to_real_prefix_size
/****************************************************************************
* _real_to_prot_suffix
*
* Trampoline fragment. Switch from 16-bit real-mode to 32-bit
* protected mode with flat physical addresses. Copy returned stack
* parameters to output_stack. Restore registers preserved by
* _prot_to_real_prefix. Restore stack to previous location.
****************************************************************************
*/
.globl _real_to_prot_suffix
.code16
_real_to_prot_suffix:
/* Switch to protected mode */
call _real_to_prot
.code32
/* Calculate offset */
call 1f
1: popl %ebp
subl $1b, %ebp /* %ebp = offset for labels in r2p */
/* Copy stack to out_stack */
movl r2p_out_stack(%ebp), %edi
movl r2p_out_stack_len(%ebp), %ecx
movl %esp, %esi
cld
rep movsb
/* Switch back to original stack */
movl r2p_esp(%ebp), %esp
/* Restore registers and return */
pushl r2p_ret_addr(%ebp) /* Set up return address on stack */
movl r2p_ebx(%ebp), %ebx
movl r2p_esi(%ebp), %esi
movl r2p_edi(%ebp), %edi
movl r2p_ebp(%ebp), %ebp
ret
/****************************************************************************
* _real_to_prot
*
* Switch from 16-bit real-mode to 32-bit protected mode with flat
* physical addresses. All segment registers are destroyed, %eip and
* %esp are changed to flat physical values, all other registers are
* preserved. Interrupts are disabled.
*
* Parameters: none
****************************************************************************
*/
#define R2P_PRESERVE ( 12 )
#define R2P_OFFSET_RETADDR ( R2P_PRESERVE )
#define R2P_OFFSET_ORIG_RETADDR ( R2P_OFFSET_RETADDR + 2 )
.globl _real_to_prot
.code16
_real_to_prot:
/* Disable interrupts */
cli
/* zero extend the return address */
pushw $0
/* Preserve registers */
pushl %ebp
pushl %ebx
pushl %eax
/* Convert 16-bit real-mode near return address to
* 32-bit pmode physical near return address
*/
movw %sp, %bp
xorl %ebx, %ebx
push %cs
popw %bx
movw %bx, %ds
shll $4, %ebx
movzwl %ss:R2P_OFFSET_ORIG_RETADDR(%bp), %eax
addl %ebx, %eax
movl %eax, %ss:(R2P_OFFSET_RETADDR)(%bp)
/* Store the code segment physical base address in %ebp */
movl %ebx, %ebp
/* Find the offset within the code segment that I am running at */
xorl %ebx, %ebx
call 1f
1: popw %bx
/* Set up GDT */
leal (r2p_gdt-1b)(%bx), %eax /* %ds:ebx = %ds:bx = &(r2p_gdt) */
addl %ebp, %eax /* %eax = &r2p_gdt (physical) */
movl %eax, %ds:(r2p_gdt-1b+2)(%bx) /* Set phys. addr. in r2p_gdt */
/* Compute the first protected mode physical address */
leal (2f-1b)(%bx), %eax
addl %ebp, %eax
movl %eax, %ds:(r2p_paddr-1b)(%bx)
/* Calculate new %esp */
xorl %eax, %eax
push %ss
popw %ax
shll $4, %eax
movzwl %sp, %ebp
addl %eax, %ebp /* %ebp = new %esp */
/* Load GDT */
DATA32 lgdt %ds:(r2p_gdt-1b)(%bx) /* Load GDT */
/* Switch to protected mode */
movl %cr0, %eax
orb $CR0_PE, %al
movl %eax, %cr0
/* flush prefetch queue, and reload %cs:%eip */
DATA32 ljmp %ds:*(r2p_paddr-1b)(%bx)
.code32
2:
/* Load segment registers, adjust %esp */
movw $(r2p_pmds-r2p_gdt), %ax
movw %ax, %ss
movl %ebp, %esp
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
/* Restore registers */
popl %eax
popl %ebx
popl %ebp
/* return to caller */
ret
r2p_gdt:
.word r2p_gdt_end - r2p_gdt - 1 /* limit */
.long 0 /* addr */
.word 0
r2p_pmcs:
/* 32 bit protected mode code segment, physical addresses */
.word 0xffff, 0
.byte 0, 0x9f, 0xcf, 0
r2p_pmds:
/* 32 bit protected mode data segment, physical addresses */
.word 0xffff,0
.byte 0,0x93,0xcf,0
r2p_gdt_end:
r2p_paddr:
.long 2b
.word r2p_pmcs - r2p_gdt, 0
/* This is the end of the trampoline suffix code.
*/
r2p_params: /* Structure must match real_to_prot_params_t in realmode.h */
r2p_ret_addr: .long 0
r2p_esp: .long 0
r2p_ebx: .long 0
r2p_esi: .long 0
r2p_edi: .long 0
r2p_ebp: .long 0
r2p_out_stack: .long 0
r2p_out_stack_len: .long 0
.globl _real_to_prot_suffix_end
_real_to_prot_suffix_end:
.globl _real_to_prot_suffix_size
.equ _real_to_prot_suffix_size, _real_to_prot_suffix_end - _real_to_prot_suffix
.globl real_to_prot_suffix_size
real_to_prot_suffix_size:
.word _real_to_prot_suffix_size
rm_callback_interface_end:
.globl _rm_callback_interface_size
.equ _rm_callback_interface_size, rm_callback_interface_end - rm_callback_interface
.globl rm_callback_interface_size
rm_callback_interface_size:
.word _rm_callback_interface_size
/****************************************************************************
* END OF REAL-MODE CALLBACK INTERFACE
****************************************************************************
*/
#ifdef PXE_EXPORT
/****************************************************************************
* PXE CALLBACK INTERFACE
*
* Prepend this to rm_callback_interface to create a real-mode PXE
* callback interface.
****************************************************************************
*/
.section ".text16", "ax", @progbits
.globl pxe_callback_interface
.code16
pxe_callback_interface:
/* Macro to calculate offset of labels within code segment in
* installed copy of code.
*/
#define INSTALLED(x) ( (x) - pxe_callback_interface )
/****************************************************************************
* PXE entry points (!PXE and PXENV+ APIs)
****************************************************************************
*/
/* in_call mechanism for !PXE API calls */
.globl _pxe_in_call_far
_pxe_in_call_far:
/* Prepend "PXE API call" and "API version 0x201" to stack */
pushl $0x201
jmp 1f
/* in_call mechanism for PXENV+ API calls */
.globl _pxenv_in_call_far
_pxenv_in_call_far:
/* Prepend "PXE API call" and "API version 0x200" to stack */
pushl $0x200
1: pushl $EB_OPCODE_PXE
/* Perform real-mode in_call */
call pxe_rm_in_call
/* Return */
addw $8, %sp
lret
/****************************************************************************
* PXE installation check (INT 1A) code
****************************************************************************
*/
.globl _pxe_intercept_int1a
_pxe_intercept_int1a:
pushfw
cmpw $0x5650, %ax
jne 2f
1: /* INT 1A,5650 - Intercept */
popfw
/* Set up return values according to PXE spec: */
movw $0x564e, %ax /* AX := 564Eh (VN) */
pushw %cs:INSTALLED(_pxe_pxenv_segment)
popw %es /* ES:BX := &(PXENV+ structure) */
movw %cs:INSTALLED(_pxe_pxenv_offset), %bx
clc /* CF is cleared */
lret $2 /* 'iret' without reloading flags */
2: /* INT 1A,other - Do not intercept */
popfw
ljmp %cs:*INSTALLED(_pxe_intercepted_int1a)
.globl _pxe_intercepted_int1a
_pxe_intercepted_int1a: .word 0,0
.globl _pxe_pxenv_location
_pxe_pxenv_location:
_pxe_pxenv_offset: .word 0
_pxe_pxenv_segment: .word 0
pxe_rm_in_call:
pxe_attach_rm:
/* rm_callback_interface must be appended here */
pxe_callback_interface_end:
.globl _pxe_callback_interface_size
.equ _pxe_callback_interface_size, pxe_callback_interface_end - pxe_callback_interface
.globl pxe_callback_interface_size
pxe_callback_interface_size:
.word _pxe_callback_interface_size
#else /* PXE_EXPORT */
/* Define symbols used by the linker scripts, to prevent link errors */
.globl _pxe_callback_interface_size
.equ _pxe_callback_interface_size, 0
#endif /* PXE_EXPORT */
#else /* CODE16 */
/* Define symbols used by the linker scripts, to prevent link errors */
.globl _rm_callback_interface_size
.equ _rm_callback_interface_size, 0
.globl _pxe_callback_interface_size
.equ _pxe_callback_interface_size, 0
#endif /* CODE16 */